home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / pine3.92 / pine / folder.c < prev    next >
C/C++ Source or Header  |  1996-03-14  |  141KB  |  5,035 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: folder.c,v 4.218 1996/03/15 07:13:42 hubert Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    USENET News reading additions in part by L Lundblade / NorthWestNet, 1993
  32.    lgl@nwnet.net
  33.  
  34.    Pine is in part based on The Elm Mail System:
  35.     ***********************************************************************
  36.     *  The Elm Mail System  -  Revision: 2.13                             *
  37.     *                                                                     *
  38.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  39.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  40.     ***********************************************************************
  41.  
  42.  
  43.   ----------------------------------------------------------------------*/
  44.  
  45. /*======================================================================
  46.      folder.c
  47.  
  48.   Screen to display and manage all the users folders
  49.  
  50. This puts up a list of all the folders in the users mail directory on
  51. the screen spacing it nicely. The arrow keys move from one to another
  52. and the user can delete the folder or select it to change to or copy a
  53. message to. The dispay lets the user scroll up or down a screen full,
  54. or search for a folder name.
  55.  ====*/
  56.  
  57.  
  58. #include "headers.h"
  59.  
  60. #define    CLICKHERE    "[ Select Here to See Expanded List ]"
  61. #define    CLICKHERETOO    "[ ** Empty List **  Select Here to Try Re-Expanding ]"
  62. #define    CLICKHERETOONEWS \
  63.     "[ ** Empty List **  Use \"A Subscribe\" to subscribe to a newsgroup ]"
  64. #define    ALL_FOUND(X)    (((X)->use&CNTXT_NOFIND) == 0 && \
  65.               ((X)->use&CNTXT_PARTFIND) == 0)
  66. #define    FLDR_NAME(X)    ((X) ? ((X)->nickname ? (X)->nickname : (X)->name) :"")
  67. #define    SUBSCRIBE_PMT    \
  68.             "Enter full group name or substring of group to get list: "
  69.  
  70. /*----------------------------------------------------------------------
  71.    The data needed to redraw the folders screen, including the case where the 
  72. screen changes size in which case it may recalculate the folder_display.
  73.   ----*/
  74.  
  75. /* BUG: this strategy is doomed to fail when resize happens during 
  76.  *      printing that refigures line numbers and such as they're
  77.  *      stored with actual folder data (ie only one postition at a time).
  78.  */
  79. typedef struct folder_screen_state
  80. {
  81.     CONTEXT_S *context_list;        /* list of context's to display */
  82.     CONTEXT_S *context;            /* current context              */
  83.     int        folder_index;        /* current index in context     */
  84.     CONTEXT_S *prev_context;        /* previous context              */
  85.     int        prev_index;        /* perv index in prev context     */
  86.     int        display_cols;        /* number of columns on display   */
  87.     int        display_rows;        /* number of rows on display      */
  88.     int           top_row;            /* folder list row at top left    */
  89.     int        last_row;        /* last row of folder list        */
  90. } FSTATE_S;
  91.  
  92. static FSTATE_S *fs;
  93.  
  94. typedef enum {NotChecked, NotInCache, Found, Missing, End} NgCacheReturns;
  95.  
  96.  
  97. /*
  98.  * Global's used by context_mailbox and context_bboard to tie foldernames
  99.  * returned by c-client into the proper folder list
  100.  */
  101. extern void       *find_folder_list;
  102. extern MAILSTREAM *find_folder_stream;
  103. extern long        find_folder_count;
  104. extern int       find_folder_inbox;
  105. #ifdef NEWBB
  106. extern void       *newbb_folder_list;
  107. #endif
  108.  
  109.  
  110. /* short definition to keep compilers happy */
  111. typedef    int (*QSFunc) PROTO((const QSType *, const QSType *));
  112.  
  113.  
  114. /*
  115.  * Internal prototypes
  116.  */
  117. void       redraw_folder_screen PROTO(());
  118. void       display_folder PROTO((FSTATE_S *, CONTEXT_S *, int, CONTEXT_S *, \
  119.                  int));
  120. int       folder_scroll_up PROTO((long));
  121. int       folder_scroll_down PROTO((long));
  122. int       folder_scroll_to_pos PROTO((long));
  123. void       paint_folder_name PROTO((int, FSTATE_S *, int, CONTEXT_S *));
  124. char      *folder_list_entry PROTO((FSTATE_S *, int, int *, CONTEXT_S **));
  125. void       folder_insert_index PROTO((FOLDER_S *, int, void *));
  126. int        folder_total PROTO((void *));
  127. int        off_folder_display PROTO((FSTATE_S *, int, CONTEXT_S *));
  128. char      *add_new_folder PROTO((int, CONTEXT_S *));
  129. char      *group_subscription PROTO((int, CONTEXT_S *));
  130. char      *rename_folder PROTO((int, int, CONTEXT_S *));
  131. int        delete_folder PROTO((int, CONTEXT_S *, int *));
  132. void       print_folders PROTO((FSTATE_S *));
  133. int        search_folders PROTO((FSTATE_S *, int));
  134. int        compare_names PROTO((const QSType *, const QSType *));
  135. int        compare_sizes PROTO((const QSType *, const QSType *));
  136. int        compare_folders PROTO((const QSType *, const QSType *));
  137. int        compare_folders_new PROTO((const QSType *, const QSType *));
  138. void       create_folder_display PROTO((FSTATE_S *, int));
  139. void      *new_folder_list PROTO(());
  140. void       free_folder_list PROTO((void **));
  141. void      *find_folder_names PROTO((char *, char *));
  142. void       free_folders_in_context PROTO((CONTEXT_S *));
  143. FOLDER_S  *new_folder PROTO((char *));
  144. void       folder_delete PROTO((int, void *));
  145. void       update_news_prefix PROTO((MAILSTREAM *,  struct folder *));
  146. char      *get_post_list PROTO((char **));
  147. int        sort_folder_list PROTO((void  *, QSFunc));
  148. int       folder_has_recent PROTO((MAILSTREAM **, CONTEXT_S *, FOLDER_S *));
  149. NgCacheReturns chk_newsgrp_cache PROTO((char *));
  150. void       add_newsgrp_cache PROTO((char *, NgCacheReturns));
  151. #ifdef    _WINDOWS
  152. int       folder_scroll_callback PROTO((int,long));
  153. #endif
  154. #ifdef NEWBB
  155. void       clear_new_groups PROTO((void *));
  156. #endif
  157.  
  158.  
  159.  
  160. static struct key folder_keys[] =
  161.        {{"?","Help",KS_SCREENHELP},      {"O","OTHER CMDS",KS_NONE},
  162.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE},
  163.     {"P","PrevFldr",KS_NONE},    {"N","NextFldr",KS_NONE},
  164.     {"-","PrevPage",KS_PREVPAGE},    {"Spc","NextPage",KS_NEXTPAGE},
  165.     {"D","Delete",KS_NONE},        {"A","Add",KS_NONE},
  166.     {"R","Rename",KS_NONE},        {"W","WhereIs",KS_NONE},
  167.  
  168.     {"?","Help",KS_NONE},        {"O","OTHER CMDS",KS_NONE},
  169.     {"Q","Quit",KS_EXIT},        {"C","Compose",KS_COMPOSER},
  170.     {NULL,NULL,KS_NONE},        {"G","GotoFldr",KS_GOTOFLDR},
  171.     {"I","CurIndex",KS_FLDRINDEX},    {"W","WhereIs",KS_WHEREIS},
  172.     {"Y","prYnt",KS_PRINT},        {NULL,NULL,KS_NONE},
  173.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE}};
  174. INST_KEY_MENU(folder_keymenu, folder_keys);
  175. #define    MAIN_KEY      2  /* Sometimes Main, sometimes Exit */
  176. #define    SELECT_KEY    3  /* Sometimes View, sometimes Select */
  177. #define    OTHER_KEY    1
  178. #define    DELETE_KEY    8
  179. #define    ADD_KEY            9
  180. #define    RENAME_KEY    10
  181. #define    WHEREIS_KEY    11
  182.  
  183.  
  184.  
  185. /*----------------------------------------------------------------------
  186.       Front end to folder lister when it's called from the main menu
  187.  
  188.  Args: pine_state -- The general pine_state data structure
  189.  
  190.  Result: runs folder_lister
  191.  
  192.   ----*/
  193. void
  194. folder_screen(pine_state)
  195.     struct pine *pine_state;
  196. {
  197.     dprint(1, (debugfile, "=== folder_screen called ====\n"));
  198.     mailcap_free(); /* free resources we won't be using for a while */
  199.     pine_state->next_screen = main_menu_screen;
  200.     folder_lister(pine_state, FolderMaint, NULL, NULL, NULL, NULL,
  201.                   pine_state->context_list, NULL);
  202.     pine_state->prev_screen = folder_screen;
  203. }
  204.  
  205.  
  206.  
  207. /*----------------------------------------------------------------------
  208.          Browse folders for ^T selection from the composer
  209.   
  210.  Args: error_mess -- pointer to place to return an error message
  211.   
  212.  Returns: result if folder selected, NULL if not
  213.       Composer expects the result to be alloc'd here 
  214.  
  215.   ----*/     
  216. char *
  217. folders_for_fcc(error_mess)
  218.      char **error_mess;
  219. {
  220.     char       tmp[MAXPATH], *rs = NULL;
  221.     int        rv;
  222.     CONTEXT_S *cntxt = ps_global->context_current;
  223.  
  224.     /* Coming back from composer */
  225.     fix_windsize(ps_global);
  226.     init_sigwinch();
  227.  
  228.     /*
  229.      * if folder_lister returns ambiguous name IF the chosen context
  230.      * matches the default save context, OTHERWISE it returns the 
  231.      * selected folder's fully qualified name.
  232.      *
  233.      * Note: this could stand to be cleaned up.  That is, it would be
  234.      * nice for the user to be shown the context along with the 
  235.      * folder name rather than the raw IMAP name.  Perhaps, someday
  236.      * adding understanding of nicknames perhaps with <nickname>folder
  237.      * syntax similar to the titlebar makes sense...
  238.      */
  239.     if(rv = folder_lister(ps_global, GetFcc, NULL, &cntxt, tmp, NULL,
  240.               ps_global->context_list, NULL)){
  241.     if(context_isambig(tmp) && !((cntxt->use) & CNTXT_SAVEDFLT)){
  242.         context_apply(rs = tmp_20k_buf, cntxt->context, tmp);
  243.  
  244.         /*
  245.          * A bit of a problem here.  If the foldername applied
  246.          * to the context is still relative, we can probably assume 
  247.          * the folder's relative to ~, so add it here for now.
  248.          * NOTE: this WILL break if a local driver is added, that
  249.          * isn't file system based...
  250.          */
  251.         if(context_isambig(rs))
  252.           build_path(rs = tmp, ps_global->ui.homedir, tmp_20k_buf);
  253.     }
  254.     else
  255.       rs = tmp;
  256.     }
  257.  
  258.     return(rs ? cpystr(rs) : NULL);
  259. }
  260.  
  261.     
  262.  
  263. /*----------------------------------------------------------------------
  264.       Main routine for the list of folders screen, displays and execute cmds.
  265.  
  266.   Args:  ps            -- The pine_state data structure
  267.          do_what       -- What function we're called as -- select, maint...
  268.          return_string -- Buffer to return selected folder name in
  269.          return_array  -- Return *alloced* array of selected folder names here
  270.      return_context -- Context that the return_string makes is applied to
  271.      start_context -- Context to first display
  272.  
  273.   Result: return 0 for abort, 1 for open folder, and 2 for save folder
  274.           The name selected is copied into the given buffer
  275.  
  276. This code assume that the folder list will have one folder in it and
  277. is likey to crash if there isn't one.
  278.  
  279.   ----*/
  280. int 
  281. folder_lister(ps, do_what, start_context, return_context, return_string,
  282.               return_array, context_list, f_state)
  283.     struct pine *ps;
  284.     FolderFun    do_what;
  285.     CONTEXT_S   *start_context;
  286.     CONTEXT_S  **return_context;
  287.     CONTEXT_S   *context_list;
  288.     char        *return_string;
  289.     char      ***return_array;
  290.     void        *f_state;
  291. {
  292.     int              ch, orig_ch, mangled_footer, mangled_header, km_size,
  293.                      rv, quest_line, rc, cur_row, cur_col, km_popped,
  294.              doing_listmode;
  295.     unsigned short   new_col;
  296.     CONTEXT_S       *tc;
  297.     char            *new_file, *cur_name, *new_fold;
  298.     FOLDER_S        *cur_f;
  299.     struct key_menu *km;
  300.     bitmap_t         bitmap;
  301.     OtherMenu        what;
  302.     FSTATE_S         default_fstate;
  303.  
  304.     dprint(1, (debugfile, "\n\n    ---- FOLDER SCREEN ----\n"));
  305.  
  306.     fs                 = f_state ? (FSTATE_S *)f_state : &default_fstate;
  307.     quest_line         = -FOOTER_ROWS(ps);
  308.     fs->prev_index     = -1;
  309.     fs->prev_context   = NULL;
  310.     fs->top_row           = 0;
  311.     mangled_footer     = 1;
  312.     mangled_header     = 1;
  313.     what               = FirstMenu;
  314.     km_popped          = 0;
  315.     doing_listmode     = 0;
  316.     fs->context_list   = context_list;
  317.     fs->context        = start_context ? start_context 
  318.                        : (ps->context_current) 
  319.                      ? ps->context_current
  320.                      : context_list;
  321.  
  322.     /*
  323.      * The subscription handler's already done the find, so
  324.      * just expand if it's the default, there's only one context,
  325.      * or we're here to select something...
  326.      */
  327.     if(do_what != Subscribe){
  328.     if(F_ON(F_EXPANDED_FOLDERS,ps_global) || !context_list->next){
  329.         for(tc = context_list; tc ; tc = tc->next)
  330.           find_folders_in_context(NULL, tc, NULL);
  331.     }
  332.     else if(do_what != FolderMaint && do_what != Subscribe)
  333.       find_folders_in_context(NULL, fs->context, NULL);
  334.     }
  335.  
  336.     if(do_what == Subscribe || do_what == PostNews
  337.        || (fs->folder_index = folder_index(ps->cur_folder, 
  338.                        fs->context->folders)) < 0)
  339.       fs->folder_index = 0;
  340.  
  341.     ClearBody();
  342.  
  343.     create_folder_display(fs, ps->ttyo->screen_cols);
  344.  
  345.     ps->redrawer = redraw_folder_screen;
  346.  
  347.     for(ch = 'y' /* For display_message first time through */;;) {
  348.  
  349.     if(km_popped){
  350.         km_popped--;
  351.         if(km_popped == 0){
  352.         clearfooter(ps);
  353.         fs->prev_index = -1;
  354.         }
  355.     }
  356.  
  357.         /*------------ New mail check ----------*/
  358.         if(ps->ttyo->screen_rows > 1 && new_mail(0, NM_TIMING(ch), 1) >= 0)
  359.       mangled_header = 1;
  360.  
  361.         if(streams_died())
  362.           mangled_header = 1;
  363.  
  364.         /*----------  screen painting -------------*/
  365.     if(mangled_header && ps->ttyo->screen_rows > 0) {
  366.         mangled_header = 0;
  367.             switch(do_what) {
  368.               case FolderMaint:
  369.             set_titlebar("FOLDER LIST", ps->mail_stream,
  370.                  ps->context_current,ps->cur_folder,
  371.                  ps->msgmap, 1, FolderName, 0, 0);
  372.                 break;
  373.  
  374.               case OpenFolder:
  375.             set_titlebar("GOTO: SELECT FOLDER", ps->mail_stream,
  376.                  ps->context_current, ps->cur_folder,
  377.                  ps->msgmap, 1, FolderName, 0, 0);
  378.                 break;
  379.  
  380.               case SaveMessage:
  381.             set_titlebar("SAVE: SELECT FOLDER", ps->mail_stream,
  382.                  ps->context_current, ps->cur_folder, ps->msgmap,
  383.                  1, MessageNumber, 0, 0);
  384.                 break;
  385.  
  386.               case GetFcc:
  387.                 set_titlebar("FCC: SELECT FOLDER", ps->mail_stream,
  388.                  ps->context_current, ps->cur_folder, ps->msgmap,
  389.                  1, FolderName, 0, 0);
  390.                 break;
  391.  
  392.               case Subscribe:
  393.                 set_titlebar("SUBSCRIBE: SELECT FOLDER", ps->mail_stream,
  394.                  ps->context_current, ps->cur_folder, ps->msgmap,
  395.                  1, FolderName, 0, 0);
  396.                 break;
  397.  
  398.               case PostNews:
  399.                 set_titlebar("NWSGRP: SELECT GROUP", ps->mail_stream,
  400.                  ps->context_current, ps->cur_folder, ps->msgmap,
  401.                  1, FolderName, 0, 0);
  402.                 break;
  403.             }
  404.     }
  405.  
  406.     /*
  407.      * display_folders handles all display painting.
  408.      * only the first time thru (or if screen parms change)
  409.      * do we need to explicitly tell it to redraw.  Otherwise
  410.      * it handles framing the page and highlighting the current
  411.      * folder...
  412.      */
  413.     if(fs->prev_index != fs->folder_index
  414.        || fs->prev_context != fs->context
  415.        || (do_what == Subscribe && ch == 'x')
  416.        || (do_what == Subscribe && doing_listmode
  417.            && (ch == ctrl('M') || ch == ctrl('J'))))
  418.       display_folder(fs, fs->context, fs->folder_index,
  419.              fs->prev_context, fs->prev_index);
  420.  
  421.     if(fs->prev_context && fs->prev_context != fs->context){
  422.         q_status_message1(SM_ORDER, 0, 3, "Now in collection <%s>", 
  423.                   fs->context->label[0]);
  424.  
  425.         if(do_what == FolderMaint && (fs->prev_context 
  426.            && ((fs->prev_context->use&CNTXT_PSEUDO)
  427.                != (fs->context->use&CNTXT_PSEUDO))))
  428.           mangled_footer++;
  429.             if((fs->prev_context->type & FTYPE_BBOARD) ^
  430.                (fs->context->type & FTYPE_BBOARD))
  431.               mangled_footer++;
  432.     }
  433.  
  434.     if(mangled_footer && ps->ttyo->screen_rows > HEADER_ROWS(ps) + 1) {
  435.         setbitmap(bitmap);
  436.         km = &folder_keymenu;
  437.             folder_keys[DELETE_KEY].name = "D";
  438.             folder_keys[ADD_KEY].name = "A";
  439.             folder_keys[RENAME_KEY].name = "R";
  440.         folder_keys[RENAME_KEY].label = "Rename";
  441.             if(fs->context->type & FTYPE_BBOARD) {
  442.                 folder_keys[ADD_KEY].label = "Subscribe";
  443.                 folder_keys[DELETE_KEY].label = "UnSbscrbe";
  444.         KS_OSDATASET(&folder_keys[DELETE_KEY], KS_NONE);
  445.             } else {
  446.                 folder_keys[ADD_KEY].label = "Add";
  447.                 folder_keys[DELETE_KEY].label = "Delete";
  448.         KS_OSDATASET(&folder_keys[DELETE_KEY], KS_NONE);
  449.             }
  450.         if(do_what == FolderMaint){
  451.           km->how_many = 2;
  452.           folder_keys[MAIN_KEY].name = "M";
  453.           folder_keys[MAIN_KEY].label = "Main Menu";
  454.           KS_OSDATASET(&folder_keys[MAIN_KEY], KS_MAINMENU);
  455.           folder_keys[SELECT_KEY].name = "V";
  456.           folder_keys[SELECT_KEY].label = 
  457.            (fs->context->use & CNTXT_PSEUDO) ? "[Select]":"[ViewFldr]";
  458.           KS_OSDATASET(&folder_keys[SELECT_KEY], KS_NONE);
  459.           clrbitn(WHEREIS_KEY, bitmap); /* the one in the 1st menu */
  460.         }
  461.         else {
  462.           km->how_many = 1;
  463.           folder_keys[MAIN_KEY].name = "E";
  464.           folder_keys[MAIN_KEY].label = do_what != Subscribe ? 
  465.                                                "ExitSelect" : "ExitSubscb";
  466.  
  467.           folder_keys[SELECT_KEY].name = "S";
  468.           folder_keys[SELECT_KEY].label = do_what != Subscribe ?
  469.                                                "[Select]" : "[Subscribe]";
  470.           KS_OSDATASET(&folder_keys[SELECT_KEY], KS_NONE);
  471.           clrbitn(OTHER_KEY, bitmap);
  472.           clrbitn(RENAME_KEY, bitmap);
  473.           if(do_what == Subscribe){
  474.           if(doing_listmode){
  475.               folder_keys[DELETE_KEY].name = "X";
  476.               folder_keys[DELETE_KEY].label = "[Set/Unset]";
  477.               folder_keys[SELECT_KEY].label = "Subscribe";
  478.           }
  479.           else{
  480.               folder_keys[DELETE_KEY].name = "L";
  481.               folder_keys[DELETE_KEY].label = "ListMode";
  482.           }
  483.           }
  484.           else
  485.             clrbitn(DELETE_KEY, bitmap);
  486.  
  487. #ifdef NEWBB
  488.               if(do_what == Subscribe) {
  489.                   folder_keys[ADD_KEY].name = "X";
  490.                   folder_keys[ADD_KEY].label = "DismissNew";
  491.               } else {
  492.                clrbitn(ADD_KEY, bitmap);
  493.               }
  494. #else
  495.           clrbitn(ADD_KEY, bitmap);
  496. #endif
  497.  
  498.         }
  499.  
  500.         if(km_popped){
  501.         FOOTER_ROWS(ps) = 3;
  502.         clearfooter(ps);
  503.         }
  504.  
  505.         draw_keymenu(km, bitmap, ps->ttyo->screen_cols,
  506.         1-FOOTER_ROWS(ps), 0, what, 0);
  507.         mangled_footer = 0;
  508.         what           = SameTwelve;
  509.         if(km_popped){
  510.         FOOTER_ROWS(ps) = 1;
  511.         mark_keymenu_dirty();
  512.         }
  513.     }
  514.  
  515.         fs->prev_index   = fs->folder_index;
  516.         fs->prev_context = fs->context;
  517.     if(folder_total(fs->context->folders)){
  518.         cur_f = folder_entry(fs->folder_index, fs->context->folders);
  519.         cur_name = FLDR_NAME(cur_f);
  520.     }
  521.     else
  522.      cur_f  = NULL;
  523.  
  524.         /*------- display any status messages -----*/
  525.     if(km_popped){
  526.         FOOTER_ROWS(ps) = 3;
  527.         mark_status_unknown();
  528.     }
  529.  
  530.     display_message(ch);
  531.     if(km_popped){
  532.         FOOTER_ROWS(ps) = 1;
  533.         mark_status_unknown();
  534.     }
  535.  
  536.     if(F_ON(F_SHOW_CURSOR, ps) && cur_f){
  537.         cur_row = HEADER_ROWS(ps) + (cur_f->d_line - fs->top_row);
  538.         cur_col = cur_f->d_col;
  539.     }
  540.     else{
  541.         cur_row = ps->ttyo->screen_rows - FOOTER_ROWS(ps);
  542.         cur_col = 0;
  543.     }
  544.  
  545.         /*----- Read and validate the next command ------*/
  546.     MoveCursor(cur_row, cur_col);
  547.  
  548. #ifdef    MOUSE
  549.     mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);/* prime the handler */
  550.     register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
  551.               ps_global->ttyo->screen_rows-(FOOTER_ROWS(ps_global)+1),
  552.               ps_global->ttyo->screen_cols);
  553. #endif
  554. #ifdef    _WINDOWS
  555.     mswin_setscrollcallback(folder_scroll_callback);
  556. #endif
  557.     ch = read_command();
  558. #ifdef    MOUSE
  559.     clear_mfunc(mouse_in_content);
  560. #endif
  561. #ifdef    _WINDOWS
  562.     mswin_setscrollcallback(NULL);
  563. #endif
  564.         orig_ch = ch;
  565.         
  566.     if(ch < 'z' && isupper(ch))
  567.       ch = tolower(ch);
  568.  
  569.     if(km->which == 1)
  570.           if(ch >= PF1 && ch <= PF12)
  571.             ch = PF2OPF(ch);
  572.  
  573.     ch = validatekeys(ch);
  574.  
  575.         dprint(5, (debugfile, "folder command: %c (%d)\n",ch,ch));
  576.  
  577.     if(km_popped)
  578.       switch(ch){
  579.         case NO_OP_IDLE:
  580.         case NO_OP_COMMAND: 
  581.         case PF2:
  582.         case OPF2:
  583.         case 'o' :
  584.         case KEY_RESIZE:
  585.         case ctrl('L'):
  586.           km_popped++;
  587.           break;
  588.         
  589.         default:
  590.           clearfooter(ps);
  591.           break;
  592.       }
  593.  
  594.         /*----------- Execute command --------------*/
  595.     switch(ch) {
  596.  
  597.             /*---------- display other key bindings ------*/
  598.           case PF2:
  599.           case OPF2:
  600.           case 'o' :
  601.             if(do_what != FolderMaint)
  602.               goto bleep;
  603.  
  604.             if (ch == 'o')
  605.           warn_other_cmds();
  606.             what = NextTwelve;
  607.             mangled_footer++;
  608.             break;
  609.  
  610.  
  611.             /*---------------------- Key left --------------*/
  612.       case ctrl('B'):
  613.       case KEY_LEFT:
  614.       case PF5:
  615.       case 'p':
  616.         if(fs->folder_index > 0 && ALL_FOUND(fs->context)){
  617.         fs->folder_index--;
  618.         }
  619.         else if(fs->context_list != fs->context){
  620.         for(tc = fs->context_list; tc->next != fs->context;
  621.             tc = tc->next);
  622.  
  623.         fs->context = tc;
  624.         if(ALL_FOUND(fs->context) && folder_total(fs->context->folders))
  625.           fs->folder_index = folder_total(fs->context->folders) - 1;
  626.         else
  627.           fs->folder_index = 0;
  628.         }
  629.         else
  630.           q_status_message(SM_ORDER,0,1,"Already on first folder.");
  631.  
  632.         break;
  633.  
  634.  
  635.             /*--------------------- Key right -------------------*/
  636.           case ctrl('F'): 
  637.           case KEY_RIGHT:
  638.       case PF6:
  639.       case 'n':
  640.           case '\t':
  641.         if(fs->folder_index + 1 < folder_total(fs->context->folders)
  642.            && ALL_FOUND(fs->context)){
  643.         fs->folder_index++;
  644.         }
  645.         else if(fs->context->next){    /* next context?*/
  646.         fs->context = fs->context->next;
  647.         fs->folder_index = 0;
  648.         }
  649.         else
  650.           q_status_message(SM_ORDER,0,1,"Already on last folder.");
  651.  
  652.             break;
  653.  
  654.  
  655.             /*--------------- Key up ---------------------*/
  656.           case KEY_UP:
  657.       case ctrl('P'):
  658.         if(!folder_total(fs->context->folders))
  659.           break;
  660.  
  661.         new_col = folder_entry(fs->folder_index, 
  662.                    fs->context->folders)->d_col;
  663.         rc        = folder_entry(fs->folder_index, 
  664.                    fs->context->folders)->d_line - 1;
  665.  
  666.         /* find next line */
  667.         while(rc >= 0 && folder_list_entry(fs, rc, &(fs->folder_index),
  668.                            &(fs->context)))
  669.           rc--;
  670.  
  671.         if(rc < 0){
  672.         q_status_message(SM_ORDER, 0, 1, "Already on first line.");
  673.         if(fs->top_row != 0){        /* make sure! */
  674.             fs->top_row    = 0;
  675.             fs->prev_index = -1;
  676.         }
  677.  
  678.         break;
  679.         }
  680.  
  681.         /* find the right column on it */
  682.         while((cur_f = folder_entry(fs->folder_index, 
  683.                      fs->context->folders))->d_col < new_col){
  684.         if(fs->folder_index+1 >= folder_total(fs->context->folders)
  685.            || folder_entry(fs->folder_index + 1,
  686.                    fs->context->folders)->d_col == 0)
  687.           break;
  688.         else
  689.           fs->folder_index++;
  690.         }
  691.  
  692.         if((rv = fs->top_row - cur_f->d_line) > 0){
  693.         int i;
  694.  
  695.         for(i=cur_f->d_line-1, rc=fs->folder_index, tc=fs->context;
  696.             i >= 0 && folder_list_entry(fs, i, &rc, &tc);
  697.             i--, rv++)
  698.           ;
  699.  
  700.         folder_scroll_down(rv);
  701.         fs->prev_index = -1;
  702.         }
  703.  
  704.             break;
  705.  
  706.  
  707.             /*----------------- Key Down --------------------*/
  708.           case KEY_DOWN:
  709.       case ctrl('N'):
  710.         if(!folder_total(fs->context->folders))
  711.           break;
  712.  
  713.         new_col = folder_entry(fs->folder_index, 
  714.                    fs->context->folders)->d_col;
  715.         rc      = folder_entry(fs->folder_index, 
  716.                    fs->context->folders)->d_line + 1;
  717.  
  718.         if(rc > fs->last_row){
  719.         q_status_message(SM_ORDER, 0, 1, "Already on last line.");
  720.         break;
  721.         }
  722.  
  723.         /* find next line */
  724.         while(rc <= fs->last_row
  725.           && folder_list_entry(fs, rc, &(fs->folder_index),
  726.                        &(fs->context)))
  727.           rc++;
  728.  
  729.         /* find the right column on it */
  730.         while(folder_entry(fs->folder_index,
  731.                    fs->context->folders)->d_col < new_col){
  732.         if(fs->folder_index+1 >= folder_total(fs->context->folders)
  733.            || folder_entry(fs->folder_index + 1, 
  734.                    fs->context->folders)->d_col == 0)
  735.           break;
  736.         else
  737.           fs->folder_index++;
  738.         }
  739.  
  740.         if((rc = folder_entry(fs->folder_index,
  741.                   fs->context->folders)->d_line
  742.                   - (fs->top_row + fs->display_rows) + 1) > 0){
  743.         fs->prev_index = -1;
  744.         folder_scroll_up(rc);
  745.         }
  746.  
  747.         break;
  748.  
  749.  
  750.             /*----------------- Beginning of Line --------------------*/
  751.       case ctrl('A'):
  752.         while(1){
  753.         if(fs->folder_index == 0
  754.            || folder_entry(fs->folder_index, 
  755.                    fs->context->folders)->d_col == 0)
  756.           break;
  757.         else
  758.           fs->folder_index--;
  759.         }
  760.  
  761.         break;
  762.  
  763.  
  764.             /*----------------- End of Line --------------------*/
  765.       case ctrl('E'):
  766.         while(1){
  767.         if(fs->folder_index+1 >= folder_total(fs->context->folders)
  768.            || folder_entry(fs->folder_index + 1, 
  769.                    fs->context->folders)->d_col == 0)
  770.           break;
  771.         else
  772.           fs->folder_index++;
  773.         }
  774.  
  775.         break;
  776.  
  777.  
  778. #ifdef    MOUSE
  779.             /*-------------- Mouse down in content ----------------------*/
  780.       case KEY_MOUSE:
  781.         {
  782.         MOUSEPRESS mp;
  783.         int new_index, ncol, tcol;
  784.         CONTEXT_S    *new_context;
  785.  
  786.         mouse_get_last (NULL, &mp);
  787.         mp.row -= HEADER_ROWS(ps);
  788.         if (mp.doubleclick) {
  789.             goto DoSelect;
  790.         }
  791.         else {
  792.  
  793.             /* Gone past end of informatoin? */
  794.             if (mp.row >= fs->last_row)
  795.               break;
  796.  
  797.             /* Clicked on one of those seperator lines? */
  798.             if (folder_list_entry(fs, mp.row + fs->top_row,
  799.                       &new_index, &new_context))
  800.               break;
  801.         
  802.             /* folder_list_entry should have given us a new context
  803.              * and index for first folder on line.  Find folder under
  804.              * the mouse click. */
  805.             while((tcol = folder_entry(new_index, 
  806.                 new_context->folders)->d_col) < mp.col) {
  807.             /* Reached end of folders?  */
  808.             if (new_index+1 >= folder_total(new_context->folders))
  809.               goto GotCol;
  810.             /* Reached end of line? */
  811.             if ((ncol = folder_entry(new_index + 1, 
  812.                         new_context->folders)->d_col) == 0)
  813.               goto GotCol;
  814.             /* Mouse click with in column range? */
  815.             if (mp.col >= tcol && mp.col < ncol)
  816.               goto GotCol;
  817.             ++new_index;
  818.             }
  819.     GotCol:
  820.             fs->folder_index = new_index;
  821.             fs->context = new_context;
  822.         }
  823.         }
  824.         break;
  825. #endif    /* MOUSE */
  826.  
  827.             /*--------------Scroll Up ----------------------*/
  828.           case PF7: 
  829.       case KEY_PGUP:
  830.           case ctrl('Y'): 
  831.       case '-':
  832.         if(fs->top_row
  833.         || (fs->folder_index > 0 && ALL_FOUND(fs->context))
  834.             || (fs->context_list != fs->context)){
  835.         rc = max(0, fs->top_row - fs->display_rows);
  836.  
  837.         while(folder_list_entry(fs, rc, &(fs->folder_index), 
  838.                     &(fs->context)))
  839.               rc++;
  840.         } else {
  841.                 q_status_message(SM_ORDER,0,1,"Already on first page.");
  842.         }
  843.  
  844.             break;
  845.  
  846.  
  847.             /*---------- Scroll screenful ------------*/
  848.       case PF8:
  849.       case KEY_PGDN:
  850.       case SPACE: 
  851.           case ctrl('V'): 
  852.       case '+':
  853.         if(!folder_total(fs->context->folders))
  854.           break;
  855.  
  856.         if((rc = fs->top_row + fs->display_rows) > fs->last_row){
  857.         if((int)folder_entry(fs->folder_index,
  858.                 fs->context->folders)->d_line >= fs->last_row){
  859.             q_status_message(SM_ORDER,0,1,"Already on last page.");
  860.             break;
  861.         }
  862.         else
  863.           rc = fs->last_row;
  864.         }
  865.  
  866.         while(rc <= fs->last_row
  867.           && folder_list_entry(fs, rc, &(fs->folder_index),
  868.                        &(fs->context)))
  869.           rc++;
  870.  
  871.         break;
  872.  
  873.  
  874.             /*------------------ Help ----------------------*/
  875.       case PF1:
  876.       case OPF1:
  877.       case '?':
  878.       case ctrl('G'):
  879.         if(FOOTER_ROWS(ps) == 1 && km_popped == 0){
  880.         km_popped = 2;
  881.         mangled_footer = 1;
  882.         break;
  883.         }
  884.  
  885.             ps_global->next_screen = SCREEN_FUN_NULL;
  886.             ps_global->redrawer = (void (*)())NULL;
  887.         km_size = FOOTER_ROWS(ps_global);
  888.             switch(do_what) {
  889.               case FolderMaint:
  890.                 helper(h_folder_maint, "HELP FOR FOLDERS", 0);
  891.                 break;
  892.               case OpenFolder:
  893.                 helper(h_folder_open, "HELP FOR OPENING FOLDERS", 0);
  894.                 break;
  895.               case SaveMessage:
  896.                 helper(h_folder_save,"HELP FOR SAVING MESSAGES TO FOLDERS", 0);
  897.                 break;
  898.               case GetFcc:
  899.                 helper(h_folder_fcc, "HELP FOR SELECTING THE FCC", 1);
  900.                 break;
  901.               case Subscribe:
  902.                 helper(h_folder_fcc,"HELP SELECTING NEWSGROUP TO SUBSCRIBE TO",
  903.                        1);
  904.                 break;
  905.               case PostNews:
  906.                 helper(h_folder_fcc, "HELP FOR SELECTING NEWSGROUP TO POST TO",
  907.                        1);
  908.                 break;
  909.             }
  910.             if(ps_global->next_screen != SCREEN_FUN_NULL) {
  911.                 /* So "m" to go back to main menu works */
  912.             return(0);
  913.             }
  914.             ps_global->redrawer = redraw_folder_screen;
  915.         mangled_header++;
  916.         mangled_footer++;
  917.         fs->prev_index = -1;
  918.         if(km_size != FOOTER_ROWS(ps_global))  /* keymenu came or went */
  919.           create_folder_display(fs, ps_global->ttyo->screen_cols);
  920.  
  921.         break;
  922.  
  923.  
  924.             /*---------- Select or View ----------*/
  925.           case ctrl('M') :
  926.           case ctrl('J') :
  927.           case PF4:
  928.           case 'v':
  929.           case 's':
  930.         if((ch == ctrl('M') || ch == ctrl('J'))
  931.            && do_what == Subscribe && doing_listmode)
  932.           goto DoToggle;  /* different default in this case */
  933. DoSelect:
  934.         if((do_what != FolderMaint && ch == 'v') 
  935.            || (do_what == FolderMaint && ch == 's'))
  936.           goto bleep;
  937.         if(!folder_total(fs->context->folders)){
  938.         q_status_message(SM_ORDER | SM_DING, 3, 3, 
  939.                   "Empty folder collection.  Nothing to select!");
  940.         } else if(!ALL_FOUND(fs->context) 
  941.               || (fs->context->use & CNTXT_PSEUDO)){
  942.         if(fs->context->use & CNTXT_PSEUDO){
  943.             folder_delete(0, fs->context->folders);
  944.             fs->context->use &= ~CNTXT_PSEUDO;
  945.         }
  946.  
  947.         if(!folder_total(fs->context->folders))
  948.           fs->context->use |= CNTXT_NOFIND; /* ok to try find */
  949.  
  950.         find_folders_in_context(NULL, fs->context, NULL);
  951.  
  952.         if(fs->context == ps->context_current
  953.            && (fs->folder_index = folder_index(ps->cur_folder, 
  954.                             fs->context->folders)) < 0)
  955.           fs->folder_index = 0;
  956.  
  957.         create_folder_display(fs, ps_global->ttyo->screen_cols);
  958.  
  959.         fs->prev_index = -1;          /* redraw display */
  960.  
  961.         if(do_what == FolderMaint)
  962.           mangled_footer++;
  963.         } else if(do_what == FolderMaint) {
  964.         
  965.                 /*--- Open folder ---*/
  966.         if(cur_name == NULL)
  967.           break;
  968.  
  969.                 if(do_broach_folder(cur_name, fs->context) == 1) {
  970.             for(tc = fs->context_list; tc ; tc = tc->next)
  971.               free_folders_in_context(tc);
  972.  
  973.                     ps_global->redrawer = (void(*)())NULL;
  974.                     ps_global->next_screen = mail_index_screen;
  975.                     return(1); 
  976.                 }
  977.  
  978.                 /* Open Failed. Message will be issued by do_broach_folder. */
  979.                 /* Stay here in folder lister and let the user try again. */
  980.         mangled_footer++;
  981.                 break;
  982.             } else {
  983.                 /*-- save message, subscribe or post --- */
  984.                 if((do_what == GetFcc || do_what == SaveMessage)
  985.            && (fs->context->type & FTYPE_BBOARD)) {
  986.                     q_status_message(SM_ORDER | SM_DING, 3, 4,
  987.              "Can't save messages to bulletin boards or news groups!");
  988.                     break;
  989.                 }
  990.  
  991.             if(cur_name == NULL){
  992.             rv = 0;
  993.             }
  994.         else{
  995.             if(do_what == Subscribe){
  996.             int i, n=0, folder_n;
  997.  
  998.             if(return_context)
  999.               *return_context = fs->context;
  1000.  
  1001.             folder_n = folder_total(fs->context->folders);
  1002.             /* count X's */
  1003.             if(doing_listmode)
  1004.               for(i=0; i < folder_n; i++)
  1005.                 if(folder_entry(i,
  1006.                     fs->context->folders)->prefix[1] == 'X')
  1007.                   n++;
  1008.  
  1009.             if(n == 0 && !strncmp(folder_entry(fs->folder_index,
  1010.                       fs->context->folders)->prefix,"SUB",3)){
  1011.                 q_status_message1(SM_ORDER,0,4,
  1012.                 "Already subscribed to \"%s\"",
  1013.                 cur_name == NULL ? "" : cur_name);
  1014.                 break;
  1015.             }
  1016.  
  1017.             if(n == 0 && doing_listmode){
  1018.                 q_status_message(SM_ORDER,0,1,
  1019.                 "Use \"X\" to mark groups to subscribe to");
  1020.                 break;
  1021.             }
  1022.  
  1023.             /* if more than one name, return in the array */
  1024.             if(return_array && n > 1){
  1025.                 char question_buf[80];
  1026.                 int ans;
  1027.  
  1028.                 sprintf(question_buf, "Subscribe to %d new groups",
  1029.                     n);
  1030.                 if(F_OFF(F_SELECT_WO_CONFIRM,ps_global)
  1031.                   && (ans=want_to(question_buf,'n','x',NO_HELP,0,0))
  1032.                     != 'y'){
  1033.                 if(ans == 'x')
  1034.                   q_status_message(SM_ORDER,0,1,
  1035.                 "Use \"ExitSubscb\" to cancel subscribe command");
  1036.  
  1037.                 mangled_footer++;
  1038.                 break;
  1039.                 }
  1040.  
  1041.                 *return_array = (char **)fs_get(sizeof(char *)
  1042.                                 * (n+1));
  1043.                 memset((void *)*return_array,0,
  1044.                     sizeof(char *)*(n+1));
  1045.                 for(i=0,n=0; i < folder_n; i++){
  1046.                 if(folder_entry(i,
  1047.                     fs->context->folders)->prefix[1] == 'X'){
  1048.                     (*return_array)[n++]
  1049.                     = cpystr(folder_entry(i,
  1050.                           fs->context->folders)->name);
  1051.                 }
  1052.                 }
  1053.  
  1054.                 rv = 17;
  1055.             }
  1056.             else if(return_string){
  1057.                 strcpy(return_string, cur_name);
  1058.                 rv = 1;
  1059.             }
  1060.             else
  1061.               rv = 0;
  1062.             }
  1063.             else{
  1064.             if(return_context)
  1065.               *return_context = fs->context;
  1066.  
  1067.             if(return_string)
  1068.               strcpy(return_string,
  1069.                  (do_what == GetFcc && cur_f)
  1070.                    ? cur_f->name : cur_name);
  1071.  
  1072.             rv = 1;
  1073.             dprint(5, (debugfile,
  1074.                   "return \"%s\" in context \"%s\"\n",
  1075.                   (return_string) ? return_string : "NULL", 
  1076.                   (return_context) ? (*return_context)->context 
  1077.                             : "NULL"));
  1078.             }
  1079.             }
  1080.  
  1081.                 if(do_what != PostNews)
  1082.           for(tc = fs->context_list; tc ; tc = tc->next)
  1083.             free_folders_in_context(tc);
  1084.  
  1085.                 ps_global->redrawer = (void (*)())NULL;
  1086.             return(rv);
  1087.             }
  1088.  
  1089.         break;
  1090.  
  1091.  
  1092.             /*--------- Hidden "To Fldrs" command -----------*/
  1093.       case 'l':
  1094.       ListModeOn:
  1095.         if(do_what == FolderMaint)
  1096.           q_status_message(SM_ORDER, 0, 3, "Already in Folder List");
  1097.         else if(do_what == Subscribe && !doing_listmode){
  1098.         int i, folder_n;
  1099.  
  1100.         doing_listmode++;
  1101.         folder_n = folder_total(fs->context->folders);
  1102.         for(i=0; i < folder_n; i++){
  1103.             if(strncmp(folder_entry(i,fs->context->folders)->prefix,
  1104.             "SUB", 3) != 0){
  1105.             folder_entry(i, fs->context->folders)->prefix[0] = '[';
  1106.             folder_entry(i, fs->context->folders)->prefix[2] = ']';
  1107.             }
  1108.         }
  1109.  
  1110.         redraw_folder_screen();
  1111.         q_status_message(SM_ORDER,0,1,
  1112.             "Use \"X\" to mark groups to subscribe to");
  1113.         mangled_footer++;
  1114.         }
  1115.         else
  1116.           goto bleep;
  1117.  
  1118.         break;
  1119.  
  1120.     
  1121.             /*--------- EXIT menu -----------*/
  1122.       case PF3:
  1123.       case 'm':
  1124.       case 'e':
  1125.         if((do_what != FolderMaint && ch == 'm') 
  1126.            || (do_what == FolderMaint && ch == 'e'))
  1127.           goto bleep;
  1128.  
  1129.         ps_global->redrawer = (void (*)())NULL;
  1130.         for(tc = fs->context_list; tc ; tc = tc->next)
  1131.           free_folders_in_context(tc);
  1132.  
  1133.         return(0);
  1134.  
  1135.  
  1136.             /*--------- QUIT pine -----------*/
  1137.           case OPF3:
  1138.       case 'q':
  1139.             if(do_what != FolderMaint)
  1140.                 goto bleep;
  1141.  
  1142.         for(tc = fs->context_list; tc ; tc = tc->next)
  1143.           free_folders_in_context(tc);
  1144.  
  1145.             ps_global->redrawer = (void (*)())NULL;
  1146.             ps_global->next_screen = quit_screen;
  1147.         return(0);
  1148.         
  1149.  
  1150.             /*--------- Compose -----------*/
  1151.           case OPF4:
  1152.       case 'c':
  1153.             if(do_what != FolderMaint)
  1154.                 goto bleep;
  1155.  
  1156.         ps_global->redrawer = (void (*)())NULL;
  1157.         for(tc = fs->context_list; tc ; tc = tc->next)
  1158.           free_folders_in_context(tc);
  1159.  
  1160.             ps_global->next_screen = compose_screen;
  1161.         return(0);
  1162.         
  1163.  
  1164.             /*--------- Message Index -----------*/
  1165.       case OPF7:
  1166.       case 'i':
  1167.         if(do_what != FolderMaint)
  1168.           goto bleep;
  1169.  
  1170.         ps_global->redrawer = (void (*)())NULL;
  1171.         for(tc = fs->context_list; tc ; tc = tc->next)
  1172.           free_folders_in_context(tc);
  1173.  
  1174.         ps_global->next_screen = mail_index_screen;
  1175.             q_status_message(SM_ORDER, 0, 2, "Returning to current index");
  1176.         return(0);
  1177.  
  1178.  
  1179.             /*----------------- Add a new folder name -----------*/
  1180.       case PF10:
  1181.       case 'a':
  1182.             /*--------------- Rename folder ----------------*/
  1183.       case PF11:
  1184.       case 'r':
  1185.         if(do_what != FolderMaint)
  1186.           goto bleep;
  1187.  
  1188.         if(ch == 'r' || ch == PF11)
  1189.           new_file =rename_folder(quest_line,fs->folder_index,fs->context);
  1190.         else {
  1191.                 if(fs->context->type & FTYPE_BBOARD)
  1192.                   new_file = group_subscription(quest_line, fs->context);
  1193.                 else
  1194.           new_file = add_new_folder(quest_line, fs->context);
  1195.             }
  1196.  
  1197.             if(new_file && ALL_FOUND(fs->context)) {
  1198.                 /* place cursor on new folder! */
  1199.         create_folder_display(fs, ps->ttyo->screen_cols);
  1200.         fs->prev_index = -1;
  1201.         fs->folder_index = folder_index(new_file, fs->context->folders);
  1202.             }
  1203.  
  1204.         if(fs->prev_index < 0)
  1205.           mangled_header++;
  1206.  
  1207.         mangled_footer++;
  1208.             break;
  1209.              
  1210.  
  1211.             /*-------------- Delete --------------------*/
  1212.           case PF9:
  1213.       case 'd':
  1214.         if(do_what == Subscribe && ch == PF9){
  1215.         if(doing_listmode)
  1216.               goto DoToggle;
  1217.         else
  1218.           goto ListModeOn;
  1219.         }
  1220.  
  1221.         if(do_what != FolderMaint)
  1222.           goto bleep;
  1223.  
  1224.         if(!ALL_FOUND(fs->context) || (fs->context->use & CNTXT_PSEUDO)){
  1225.         q_status_message1(SM_ORDER | SM_DING, 0, 3,
  1226.                   "No folder selected to delete.  %s list.",
  1227.                   ALL_FOUND(fs->context) ? "Empty" : "Expand");
  1228.         break;
  1229.         }
  1230.  
  1231.             if(delete_folder(fs->folder_index, fs->context, &mangled_header)){
  1232.         /* remove from file list */
  1233.                 folder_delete(fs->folder_index, fs->context->folders);
  1234.  
  1235.         if(fs->folder_index >= folder_total(fs->context->folders))
  1236.           fs->folder_index = max(0, fs->folder_index - 1);
  1237.  
  1238.         create_folder_display(fs, ps->ttyo->screen_cols);
  1239.                 fs->prev_index = -1; /* Force redraw */
  1240.             }
  1241.  
  1242.             mangled_footer++;
  1243.         break;
  1244.  
  1245.  
  1246.             /*-------------- Toggle subscribe checkbox --------------------*/
  1247.       case 'x':
  1248. DoToggle:
  1249.         if(!(do_what == Subscribe && doing_listmode))
  1250.           goto bleep;
  1251.  
  1252.         if(!strncmp(folder_entry(fs->folder_index,
  1253.                     fs->context->folders)->prefix, "SUB", 3)){
  1254.         q_status_message1(SM_ORDER,0,4,
  1255.             "Already subscribed to \"%s\"",
  1256.             cur_name == NULL ? "" : cur_name);
  1257.         break;
  1258.         }
  1259.         else{
  1260.         folder_entry(fs->folder_index,fs->context->folders)->prefix[1]
  1261.             = (folder_entry(fs->folder_index,
  1262.                    fs->context->folders)->prefix[1] == 'X')
  1263.                 ? ' '
  1264.                 : 'X';
  1265.         }
  1266.  
  1267.         break;
  1268.  
  1269.  
  1270.              /*------------- Print list of folders ---------*/
  1271.           case OPF9:
  1272.       case 'y':
  1273.         if(do_what != FolderMaint)
  1274.           goto bleep;
  1275.  
  1276.             print_folders(fs);
  1277.         mangled_footer++;
  1278.         break;
  1279.  
  1280.  
  1281.             /*---------- Look (Search) ----------*/
  1282.       case OPF8:
  1283.       case PF12:
  1284.       case 'w':
  1285.       case ctrl('W'):
  1286.         if((do_what != FolderMaint && ch == OPF8) 
  1287.            || (do_what == FolderMaint && ch == PF12))
  1288.           goto bleep;
  1289.             mangled_footer++;
  1290.             rc = search_folders(fs, quest_line);
  1291.             if(rc == -1)
  1292.           q_status_message(SM_ORDER, 0, 2, "Folder name search cancelled");
  1293.         else if(rc == 0)
  1294.           q_status_message(SM_ORDER | SM_DING, 0, 2, "Word not found");
  1295.         else if(rc == 2)
  1296.               q_status_message(SM_ORDER, 0, 2, "Search wrapped to beginning");
  1297.  
  1298.         break;
  1299.  
  1300.  
  1301.             /*---------- Go to Folder, ----------*/
  1302.           case OPF6:
  1303.           case 'g':
  1304.         if(do_what != FolderMaint)
  1305.           goto bleep;
  1306.  
  1307.             new_fold = broach_folder(-FOOTER_ROWS(ps), 0, &(fs->context));
  1308.             mangled_footer = 1;;
  1309.             if(new_fold && do_broach_folder(new_fold, fs->context) > 0){
  1310.         for(tc = fs->context_list; tc ; tc = tc->next)
  1311.           free_folders_in_context(tc);
  1312.  
  1313.         ps_global->redrawer = (void (*)())NULL;
  1314.         ps_global->next_screen = mail_index_screen;
  1315.         return(1); 
  1316.         }
  1317.  
  1318.             break;
  1319.  
  1320. #ifdef NEWBB
  1321.             /*------------ Dismiss the recently created news groups -----*/
  1322.           case 'x':
  1323.             if(do_what != Subscribe)
  1324.               goto bleep;
  1325.             clear_new_groups(fs->context->folders);
  1326.             redraw_folder_screen();
  1327.             q_status_message(SM_ORDER, 0, 2,
  1328.          "Mark on newly created groups cleared; groups sorted into full list");
  1329.             break;
  1330. #endif
  1331.  
  1332.             /*----------no op to check for new mail -------*/
  1333.           case NO_OP_IDLE:
  1334.       case NO_OP_COMMAND :
  1335.         break;
  1336.  
  1337.  
  1338.             /*------------ Redraw command -------------*/
  1339.           case KEY_RESIZE:
  1340.           case ctrl('L'):
  1341.             ClearScreen();
  1342.             mangled_footer++;
  1343.             mangled_header++;
  1344.             redraw_folder_screen();
  1345.         break;
  1346.  
  1347.  
  1348.             /*--------------- Invalid Command --------------*/
  1349.       default: 
  1350.           bleep:
  1351.         bogus_command(orig_ch, F_ON(F_USE_FK,ps) ? "F1" : "?");
  1352.         break;
  1353.     } /* End of switch */
  1354.     }
  1355. }
  1356.  
  1357.  
  1358.  
  1359. /*----------------------------------------------------------------------
  1360.      Adjust the folder list's display down one line
  1361.  
  1362. Uses the static fs data structure so it can be called almost from
  1363. any context. It will recalculate the screen size if need be.
  1364.   ----*/
  1365. int
  1366. folder_scroll_down(count)
  1367.     long count;
  1368. {
  1369.     if(count < 0)
  1370.     return(folder_scroll_up(-count));
  1371.     else if(count){
  1372.     while(count-- && fs->top_row)
  1373.       fs->top_row--;
  1374.  
  1375.     if(folder_entry(fs->folder_index, fs->context->folders)->d_line
  1376.                 >= (unsigned)(fs->top_row + fs->display_rows)){
  1377.         for(count = fs->top_row + fs->display_rows - 1;
  1378.         count >= 0 && folder_list_entry(fs, (int)count,
  1379.                 &(fs->folder_index), &(fs->context)) && count >= 0;
  1380.         count--)
  1381.           ; 
  1382.  
  1383.         fs->prev_index   = fs->folder_index;
  1384.         fs->prev_context = fs->context;
  1385.     }
  1386.  
  1387.     }
  1388.  
  1389.     return(1);
  1390. }
  1391.  
  1392.  
  1393.  
  1394. /*----------------------------------------------------------------------
  1395.      Adjust the folder list's display up one line
  1396.  
  1397. Uses the static fs data structure so it can be called almost from
  1398. any context. It will recalculate the screen size if need be.
  1399.   ----*/
  1400. int
  1401. folder_scroll_up(count)
  1402.     long count;
  1403. {
  1404.     if(count < 0)
  1405.       return(folder_scroll_down(-count));
  1406.     else if(count){
  1407.     while(count-- && fs->top_row < fs->last_row)
  1408.       fs->top_row++;
  1409.  
  1410.     if(folder_entry(fs->folder_index,
  1411.             fs->context->folders)->d_line < (unsigned)fs->top_row){
  1412.         for(count = fs->top_row;
  1413.         folder_list_entry(fs, (int)count, &(fs->folder_index),
  1414.                   &(fs->context))
  1415.         && count <= folder_total(fs->context->folders);
  1416.         count++)
  1417.           ;
  1418.  
  1419.         fs->prev_index   = fs->folder_index;
  1420.         fs->prev_context = fs->context;
  1421.     }
  1422.     }
  1423.  
  1424.     return(1);
  1425. }
  1426.  
  1427.  
  1428.  
  1429. /*----------------------------------------------------------------------
  1430.      Adjust the folder list's display so the given line starts the page
  1431.  
  1432. Uses the static fs data structure so it can be called almost from
  1433. any context. It will recalculate the screen size if need be.
  1434.   ----*/
  1435. int
  1436. folder_scroll_to_pos(line)
  1437.     long line;
  1438. {
  1439.     return(folder_scroll_up(line - fs->top_row));
  1440. }
  1441.  
  1442.  
  1443.  
  1444. /*----------------------------------------------------------------------
  1445.      Redraw the folders screen
  1446.  
  1447. Uses the static fs data structure so it can be called almost from
  1448. any context. It will recalculate the screen size if need be.
  1449.   ----*/
  1450. void
  1451. redraw_folder_screen()
  1452. {
  1453.     create_folder_display(fs, ps_global->ttyo->screen_cols);
  1454.     display_folder(fs, fs->context, fs->folder_index, NULL, -1);
  1455. }
  1456.  
  1457.                 
  1458.  
  1459. /*----------------------------------------------------------------------
  1460.    Arrange and paint the lines of the folders directory on the screen
  1461.  
  1462.    Args: fd      -- The folder display structure
  1463.          lines   -- The number of folder lines to display on the screen
  1464.   
  1465.  Result: the lines are painted or repainted on the screen
  1466.  
  1467.     Paint folder list, or part of it.
  1468.  
  1469. Called to either paint or repaint the whole list, or just move the
  1470. cursor. If old_row is -1 then we are repainting. In this case have
  1471. to watch out for names that are blank as the data sometimes has blanks in
  1472. it. Go through the data row by row, column by column. Paint the item that's 
  1473. currently "it" in reverse.
  1474.  
  1475. When the changing the current one, just repaint the old one normal and the
  1476. new one reverse.
  1477.  
  1478.   ----*/
  1479. void
  1480. display_folder(fd, cntxt, index, old_cntxt, old_index)
  1481.      FSTATE_S  *fd;
  1482.      CONTEXT_S *cntxt, *old_cntxt;
  1483.      int        index, old_index;
  1484. {
  1485.     CONTEXT_S *c;
  1486.     char      *s;
  1487.     int        i, row;
  1488.  
  1489.     if(fd->display_rows <= 0)        /* room for display? */
  1490.       return;                /* nope. */
  1491.  
  1492. #ifdef _WINDOWS
  1493.     mswin_beginupdate();
  1494. #endif
  1495.     /*
  1496.      * Check the framing of the current context/folder...
  1497.      */
  1498.     if(old_index < 0 || off_folder_display(fd, index, cntxt)){
  1499.     /*------------ check framing, then... -----------*/
  1500.     while(off_folder_display(fd, index, cntxt) < 0)
  1501.       fd->top_row = max(0, fd->top_row - fd->display_rows);
  1502.  
  1503.     while(off_folder_display(fd, index, cntxt) > 0)
  1504.       fd->top_row = min(fd->last_row, fd->top_row + fd->display_rows);
  1505.  
  1506.     /*------------ repaint entire screen _-----------*/
  1507.     for(row = 0; row < fd->display_rows; row++){
  1508.         ClearLine(HEADER_ROWS(ps_global) + row);
  1509.  
  1510.         if(s = folder_list_entry(fd, row + fd->top_row, &i, &c)){
  1511.         /*-------- paint text centered --------*/
  1512.         if(*s){
  1513.             PutLine0(HEADER_ROWS(ps_global) + row,
  1514.                  max(0, (fd->display_cols/2)-(strlen(s)/2)),
  1515.                  s);
  1516.         }
  1517.         }
  1518.         else{            /* paint folder names on row */
  1519.         do
  1520.           paint_folder_name((i == index && c == cntxt), fd, i, c);
  1521.         while(++i < folder_total(c->folders)
  1522.               && folder_entry(i,c->folders)->d_line==row+fd->top_row
  1523.               && !(c->use & CNTXT_PSEUDO));
  1524.         }
  1525.     }
  1526.     }
  1527.     else{                /* restore old name to normal */
  1528.     if(folder_total(old_cntxt->folders) 
  1529.        && !off_folder_display(fd, old_index, old_cntxt))
  1530.       paint_folder_name(0, fd, old_index, old_cntxt);
  1531.  
  1532.     if(folder_total(cntxt->folders))
  1533.       paint_folder_name(1, fd, index, cntxt); /* and hilite the new name */
  1534.     }
  1535. #ifdef _WINDOWS
  1536.     scroll_setrange(fd->last_row);
  1537.     scroll_setpos(fd->top_row);
  1538.     mswin_endupdate();
  1539. #endif
  1540.     fflush(stdout);
  1541. }
  1542.  
  1543.  
  1544.  
  1545. /*
  1546.  * paint_folder_name - paint the folder at the given index in the given
  1547.  *                     collection.  
  1548.  */
  1549. void
  1550. paint_folder_name(hilite, fd, index, context)
  1551.     int        hilite, index;
  1552.     FSTATE_S  *fd;
  1553.     CONTEXT_S *context;
  1554. {
  1555.     FOLDER_S *f;
  1556.     char     *s;
  1557.  
  1558.     if((f = folder_entry(index, context->folders)) == NULL || f->name == NULL)
  1559.       return;
  1560.  
  1561.     if(context->type&FTYPE_BBOARD || context->use&CNTXT_INCMNG)
  1562.       sprintf(s = tmp_20k_buf, "%s%s", f->prefix, FLDR_NAME(f));
  1563.     else
  1564.       s = FLDR_NAME(f);
  1565.  
  1566.     MoveCursor(HEADER_ROWS(ps_global) + (f->d_line - fd->top_row), f->d_col);
  1567.     if(hilite)
  1568.       StartInverse();
  1569.  
  1570.     Write_to_screen(s);
  1571.     if(hilite)
  1572.       EndInverse();
  1573. }
  1574.  
  1575.  
  1576. #if (defined(DOS) && !defined(_WINDOWS)) || defined(OS2)
  1577. #define LINECH    '\xC4'
  1578. #else
  1579. #define LINECH    '-'
  1580. #endif
  1581.  
  1582. /*
  1583.  * folder_list_entry - return either the string associated with the
  1584.  *                     current folder list line, or the first
  1585.  *                     folder index and context associated with it.
  1586.  *
  1587.  * NOTE: this is kind of dumb right now since it starts from the top 
  1588.  *       each time it's called.  Caching the last values would make it
  1589.  *       alot less costly.
  1590.  */
  1591. char *
  1592. folder_list_entry(fd, goal_row, index, context)
  1593.     FSTATE_S   *fd;
  1594.     int         goal_row;
  1595.     int        *index;
  1596.     CONTEXT_S **context;
  1597. {
  1598.     char     *s;
  1599.     int       row, label, ftotal;
  1600.  
  1601.     if(goal_row > fd->last_row)     /* return blanks past last entry */
  1602.       return("");
  1603.  
  1604.     goal_row = max(0, goal_row);
  1605.     row      = 0;
  1606.     *index   = 0;
  1607.     for(*context = fs->context_list;
  1608.     (*context)->next && (*context)->next->d_line < goal_row;
  1609.     *context = (*context)->next)
  1610.       row = (*context)->next->d_line;
  1611.  
  1612.     s        = NULL;
  1613.     label    = (ps_global->context_list->next) ? 1 : 0;
  1614.  
  1615.     while(1){
  1616.     if(label > 0){
  1617.         if(row < (*context)->d_line){
  1618.         memset((void *)tmp_20k_buf,LINECH,fd->display_cols*sizeof(char));
  1619.         tmp_20k_buf[fd->display_cols] = '\0';
  1620.         s = (row + 1 == (*context)->d_line) ? tmp_20k_buf : "" ;
  1621.         }
  1622.         else if(label++ < 2){
  1623.         s = tmp_20k_buf;
  1624.         if((*context)->use & CNTXT_INCMNG){
  1625.             sprintf(tmp_20k_buf, "%s", (*context)->label[0]);
  1626.         }
  1627.         else{
  1628.             memset((void *)tmp_20k_buf, ' ', 
  1629.                fd->display_cols * sizeof(char));
  1630.             tmp_20k_buf[fd->display_cols] = '\0';
  1631.  
  1632.             sprintf(tmp_20k_buf + fd->display_cols + 2, 
  1633.                 "%s-collection <%s>  %s", 
  1634.                 ((*context)->type & FTYPE_BBOARD) ? "News"
  1635.                                       : "Folder",
  1636.                 (*context)->label[0], 
  1637.                 ((*context)->use & CNTXT_SAVEDFLT)
  1638.                     ? "** Default for Saves **" : "");
  1639.             strncpy(tmp_20k_buf, 
  1640.                 tmp_20k_buf + fd->display_cols + 2, 
  1641.                 strlen(tmp_20k_buf + fd->display_cols + 2));
  1642.             strncpy(tmp_20k_buf + fd->display_cols 
  1643.                 - (((*context)->type & FTYPE_REMOTE) ? 8 : 7),
  1644.                 ((*context)->type & FTYPE_REMOTE)? "(Remote)"
  1645.                                                  : "(Local)",
  1646.                 ((*context)->type & FTYPE_REMOTE) ? 8 : 7);
  1647.         }
  1648.         }
  1649.         else{
  1650.         label = -1;
  1651.         continue;
  1652.         }
  1653.     }
  1654.     else if((ftotal = folder_total((*context)->folders))
  1655.         && row < (int)folder_entry(0, (*context)->folders)->d_line){
  1656.         if(label < 0){
  1657.         memset((void *)tmp_20k_buf,LINECH,fd->display_cols*sizeof(char));
  1658.         tmp_20k_buf[fd->display_cols] = '\0';
  1659.         s = tmp_20k_buf;
  1660.         label = 0;
  1661.         }
  1662.         else
  1663.           s = "";
  1664.     }
  1665.     else{
  1666.         s          = NULL;
  1667.         if(*index >= ftotal        /* maybe continue where we left off? */
  1668.            || (int)folder_entry(*index,
  1669.                     (*context)->folders)->d_line > goal_row)
  1670.           *index = 0;
  1671.  
  1672.         for(; *index < ftotal; (*index)++)
  1673.           if(goal_row <= (int)folder_entry(*index,
  1674.                            (*context)->folders)->d_line)
  1675.         break;            /* bingo! */
  1676.  
  1677.         if(*index >= ftotal){
  1678.         *index = 0;
  1679.         if((*context)->next){
  1680.             *context = (*context)->next;
  1681.             label      = 1;
  1682.             continue;        /* go take care of labels */
  1683.         }
  1684.         else{
  1685.             s = "";            /* dropped off end of list */
  1686.             break;
  1687.         }
  1688.         }
  1689.     }
  1690.  
  1691.     if(row == goal_row)
  1692.       break;
  1693.     else
  1694.       row++;
  1695.     }
  1696.  
  1697.     *index = max(0, *index);
  1698.     return(s);
  1699. }
  1700.  
  1701.  
  1702.  
  1703. /*
  1704.  * off_folder_display - returns: 0 if given folder is on display,
  1705.  *                               1 if given folder below display, and
  1706.  *                              -1 if given folder above display
  1707.  */
  1708. int
  1709. off_folder_display(fd, index, context)
  1710.     FSTATE_S  *fd;
  1711.     int        index;
  1712.     CONTEXT_S *context;
  1713. {
  1714.     int l;
  1715.  
  1716.     if(index >= folder_total(context->folders))
  1717.       return(0);            /* no folder to display */
  1718.  
  1719.     if((l = folder_entry(index, context->folders)->d_line) < fd->top_row)
  1720.       return(-1);
  1721.     else if(l >= (fd->top_row + fd->display_rows))
  1722.       return(1);
  1723.     else
  1724.       return(0);
  1725. }
  1726.  
  1727.  
  1728.  
  1729. /*----------------------------------------------------------------------
  1730.       Create a new folder
  1731.  
  1732.    Args: quest_line  -- Screen line to prompt on
  1733.          folder_list -- The current list of folders
  1734.  
  1735.  Result: returns the name of the folder created
  1736.  
  1737.   ----*/
  1738.  
  1739. char *
  1740. add_new_folder(quest_line, cntxt)
  1741.      int        quest_line;
  1742.      CONTEXT_S *cntxt;
  1743. {
  1744.     static char  add_folder[MAXFOLDER+1];    /* needed after return!! */
  1745.     char     tmp[MAXFOLDER+1], nickname[32], c, *return_val = NULL;
  1746.     HelpType     help;
  1747.     int          rc, offset, exists, cnt = 0;
  1748.     MAILSTREAM  *create_stream;
  1749.     FOLDER_S    *f;
  1750.  
  1751.     dprint(4, (debugfile, "\n - add_new_folder - \n"));
  1752.     
  1753.     add_folder[0] = '\0';
  1754.     nickname[0]   = '\0';
  1755.     if(cntxt->use & CNTXT_INCMNG){
  1756.     char inbox_host[MAXPATH], *beg, *end = NULL;
  1757.     ESCKEY_S *special_key;
  1758.     static ESCKEY_S host_key[] = {{ctrl('X'),12,"^X","Use Inbox Host"},
  1759.                       {-1, 0, NULL, NULL}};
  1760.  
  1761.     if(ps_global->readonly_pinerc){
  1762.         q_status_message(SM_ORDER,3,5,
  1763.         "Addition cancelled: config file not editable");
  1764.         return(NULL);
  1765.     }
  1766.  
  1767.     /*
  1768.      * Prompt for the full pathname (with possible "news" subcommand),
  1769.      * then fall thru to prompt for foldername, then prompt for 
  1770.      * nick name.
  1771.      * NOTE : Don't put it in a "[<default]" prompt since we 
  1772.      * need a way to chose a local folder!
  1773.      */
  1774.     inbox_host[0] = '\0';
  1775.     if((beg = ps_global->VAR_INBOX_PATH)
  1776.        && (*beg == '{' || (*beg == '*' && *++beg == '{'))
  1777.        && (end = strindex(ps_global->VAR_INBOX_PATH, '}'))){
  1778.         strncpy(inbox_host, beg+1, end - beg);
  1779.         inbox_host[end - beg - 1] = '\0';
  1780.         special_key = host_key;
  1781.     }
  1782.     else
  1783.       special_key = NULL;
  1784.  
  1785.     sprintf(tmp, "Name of server to contain added folder : ");
  1786.     help = NO_HELP;
  1787.     while(1){
  1788.         rc = optionally_enter(add_folder, quest_line, 0, MAXFOLDER, 1,
  1789.                           0, tmp, special_key, help, 0);
  1790.         removing_trailing_white_space(add_folder);
  1791.         removing_leading_white_space(add_folder);
  1792.         if(rc == 3){
  1793.         help = help == NO_HELP ? h_incoming_add_folder_host : NO_HELP;
  1794.         }
  1795.         else if(rc == 12){
  1796.         strcpy(add_folder, inbox_host);
  1797.         break;
  1798.         }
  1799.         else if(rc == 1){
  1800.         q_status_message(SM_ORDER,0,2,
  1801.             "Addition of new folder cancelled");
  1802.         return(NULL);
  1803.         }
  1804.         else if(rc == 0)
  1805.           break;
  1806.     }
  1807.     }
  1808.  
  1809.     if(offset = strlen(add_folder)){        /* must be host for incoming */
  1810.     int i;
  1811.     sprintf(tmp, "Folder on \"%s\" to add : ", add_folder);
  1812.     for(i = offset;i >= 0; i--)
  1813.       add_folder[i+1] = add_folder[i];
  1814.  
  1815.     add_folder[0] = '{';
  1816.     add_folder[++offset] = '}';
  1817.     add_folder[++offset] = '\0';        /* +2, total */
  1818.     }
  1819.     else
  1820.       sprintf(tmp, "Name of folder to add : ");
  1821.  
  1822.     help = NO_HELP;
  1823.     while(1){
  1824.         rc = optionally_enter(&add_folder[offset], quest_line, 0, 
  1825.                   MAXFOLDER - offset, 1, 0, tmp, NULL, help, 0);
  1826.     removing_trailing_white_space(&add_folder[offset]);
  1827.     removing_leading_white_space(&add_folder[offset]);
  1828.         if(rc == 0 && add_folder[offset]){
  1829.         if(!ps_global->show_dot_names && add_folder[offset] == '.'){
  1830.         if(cnt++ <= 0)
  1831.                   q_status_message(SM_ORDER,3,3,
  1832.             "Folder name can't begin with dot");
  1833.         else{
  1834.             NAMEVAL_S *feat;
  1835.             int i;
  1836.  
  1837.             for(i=0; (feat=feature_list(i))
  1838.                 && (feat->value != F_ENABLE_DOT_FOLDERS); i++)
  1839.               ;/* do nothing */
  1840.  
  1841.             q_status_message1(SM_ORDER,3,3,
  1842.               "Config feature \"%s\" enables names beginning with dot",
  1843.               feat && feat->name ? feat->name : "");
  1844.         }
  1845.  
  1846.                 display_message(NO_OP_COMMAND);
  1847.                 continue;
  1848.         }
  1849.  
  1850.         if(strucmp(ps_global->inbox_name, nickname))
  1851.           break;
  1852.         else
  1853.           Writechar(BELL, 0);
  1854.     }
  1855.  
  1856.         if(rc == 3){
  1857.         help = (help == NO_HELP)
  1858.             ? ((cntxt->use & CNTXT_INCMNG)
  1859.                 ? h_incoming_add_folder_name
  1860.                 : h_oe_foldadd)
  1861.             : NO_HELP;
  1862.     }
  1863.     else if(rc == 1 || add_folder[0] == '\0') {
  1864.         q_status_message(SM_ORDER,0,2, "Addition of new folder cancelled");
  1865.         return(NULL);
  1866.     }
  1867.     }
  1868.  
  1869.     if(*add_folder == '{'            /* remote? */
  1870.        || (*add_folder == '*' && *(add_folder+1) == '{')){
  1871.     create_stream = context_same_stream(cntxt->context, add_folder,
  1872.                         ps_global->mail_stream);
  1873.         if(!create_stream 
  1874.            && ps_global->mail_stream != ps_global->inbox_stream)
  1875.           create_stream = context_same_stream(cntxt->context, add_folder,
  1876.                           ps_global->inbox_stream);
  1877.     }
  1878.     else
  1879.       create_stream = NULL;
  1880.  
  1881.     help = NO_HELP;
  1882.     if(cntxt->use & CNTXT_INCMNG){
  1883.     sprintf(tmp, "Nickname for folder \"%s\" : ", &add_folder[offset]);
  1884.     while(1){
  1885.         rc = optionally_enter(nickname, quest_line, 0, 31, 1, 0, tmp,
  1886.                   NULL, help, 0);
  1887.         removing_leading_white_space(nickname);
  1888.         removing_trailing_white_space(nickname);
  1889.         if(rc == 0){
  1890.         if(strucmp(ps_global->inbox_name, nickname))
  1891.           break;
  1892.         else
  1893.           Writechar(BELL, 0);
  1894.         }
  1895.  
  1896.         if(rc == 3){
  1897.         help = help == NO_HELP
  1898.             ? h_incoming_add_folder_nickname : NO_HELP;
  1899.         }
  1900.         else if(rc == 1 || (rc != 3 && !*nickname)){
  1901.         q_status_message(SM_ORDER,0,2,
  1902.             "Addition of new folder cancelled");
  1903.         return(NULL);
  1904.         }
  1905.     }
  1906.  
  1907.     /*
  1908.      * Already exist?  First, make sure this name won't collide with
  1909.      * anything else in the list.  Next, quickly test to see if it
  1910.      * the actual mailbox exists so we know any errors from 
  1911.      * context_create() are really bad...
  1912.      */
  1913.     for(offset = 0; offset < folder_total(cntxt->folders); offset++){
  1914.         f = folder_entry(offset, cntxt->folders);
  1915.         if(!strucmp(FLDR_NAME(f), nickname[0] ? nickname : add_folder)){
  1916.         q_status_message1(SM_ORDER | SM_DING, 0, 3,
  1917.                   "Incoming folder \"%s\" already exists",
  1918.                   nickname[0] ? nickname : add_folder);
  1919.         return(NULL);
  1920.         }
  1921.     }
  1922.  
  1923.     exists = folder_exists(cntxt->context, add_folder);
  1924.     }
  1925.     else
  1926.       exists = 0;
  1927.  
  1928.     if(exists < 0
  1929.        || (!exists && !context_create(cntxt->context, create_stream,
  1930.                       add_folder)))
  1931.       return(NULL);        /* c-client should've reported error */
  1932.  
  1933.     if(cntxt->use & CNTXT_INCMNG){
  1934.     f = new_folder(add_folder);
  1935.     if(nickname[0]){
  1936.         f->nickname = cpystr(nickname);
  1937.         f->name_len = strlen(f->nickname);
  1938.     }
  1939.  
  1940.     folder_insert(folder_total(cntxt->folders), f, cntxt->folders);
  1941.     if(!ps_global->USR_INCOMING_FOLDERS){
  1942.         offset = 0;
  1943.         ps_global->USR_INCOMING_FOLDERS =
  1944.                          (char **)fs_get(2*sizeof(char *));
  1945.     }
  1946.     else{
  1947.         for(offset=0;  ps_global->USR_INCOMING_FOLDERS[offset]; offset++)
  1948.           ;
  1949.  
  1950.         fs_resize((void **)&(ps_global->USR_INCOMING_FOLDERS),
  1951.               (offset + 2) * sizeof(char *));
  1952.     }
  1953.  
  1954.     sprintf(tmp, "%s%s%s%s%s", nickname[0] ? "\"" : "",
  1955.         nickname[0] ? nickname : "", nickname[0] ? "\"" : "",
  1956.         nickname[0] ? " " : "", add_folder);
  1957.     ps_global->USR_INCOMING_FOLDERS[offset]   = cpystr(tmp);
  1958.     ps_global->USR_INCOMING_FOLDERS[offset+1] = NULL;
  1959.     write_pinerc(ps_global);        /* save new element */
  1960.  
  1961.     if(nickname[0])
  1962.       strcpy(add_folder, nickname);        /* known by new name */
  1963.  
  1964.     q_status_message1(SM_ORDER, 0, 3, "Folder \"%s\" created",add_folder);
  1965.     return_val = add_folder;
  1966.     }
  1967.     else if(context_isambig(add_folder)){
  1968.     if(ALL_FOUND(cntxt)){
  1969.         if(cntxt->use & CNTXT_PSEUDO){
  1970.         folder_delete(0, cntxt->folders);
  1971.         cntxt->use &= ~CNTXT_PSEUDO;
  1972.         }
  1973.  
  1974.         folder_insert(-1, new_folder(add_folder), cntxt->folders);
  1975.         q_status_message1(SM_ORDER,0,3, "Folder \"%s\" created",add_folder);
  1976.     }
  1977.  
  1978.     return_val = add_folder;
  1979.     }
  1980.     else
  1981.       q_status_message1(SM_ORDER, 0, 3,
  1982.             "Folder \"%s\" created outside current collection",
  1983.             add_folder);
  1984.  
  1985.     return(return_val);
  1986. }
  1987.  
  1988. /*---
  1989.   subscribe context referenced here to mark appropriate entries as new
  1990.   newbb_context referenced in imap.c to know to call mark_folder_as_news
  1991.   ---*/
  1992. static    CONTEXT_S    subscribe_cntxt;
  1993. #ifdef NEWBB
  1994. static    CONTEXT_S    newbb_cntxt;
  1995. #endif
  1996.  
  1997. #ifdef NEWBB
  1998. /*----------------------------------------------------------------------
  1999.       Mark the named folder as "NEW" in subscribed_cntxt
  2000.  
  2001. Args: groups -- the name of the news group that is new
  2002.  
  2003. This is going to be inefficient on a DOS machine where folder_entry() is
  2004. expensive. 
  2005.  ----*/
  2006. void
  2007. mark_folder_as_new(group)
  2008.      char *group;
  2009. {
  2010.     int i;
  2011.  
  2012.     for(i = 0 ; i < folder_total(subscribe_cntxt.folders); i++) {
  2013.         if(strucmp(folder_entry(i, subscribe_cntxt.folders)->name,
  2014.                    group) == 0) {
  2015.             strcpy(folder_entry(i, subscribe_cntxt.folders)->prefix, "NEW ");
  2016.             break;
  2017.         }
  2018.     }
  2019. }
  2020. #endif    
  2021.  
  2022.  
  2023. /*----------------------------------------------------------------------
  2024.     Subscribe to a news group
  2025.  
  2026.    Args: quest_line  -- Screen line to prompt on
  2027.          cntxt       -- The context the subscription is for
  2028.  
  2029.  Result: returns the name of the folder subscribed too
  2030.  
  2031.  
  2032. This builds a complete context for the entire list of possible news groups. 
  2033. It also build a context to find the newly created news groups as 
  2034. determined by data kept in .pinerc.  When the find of these new groups is
  2035. done the subscribed context is searched and the items marked as new. 
  2036. A list of new board is never actually created.
  2037.  
  2038.   ----*/
  2039. char *
  2040. group_subscription(quest_line, cntxt)
  2041.      int        quest_line;
  2042.      CONTEXT_S *cntxt;
  2043. {
  2044.     char       *add_folder, *full_name;
  2045.     char      **folders;
  2046.     int            rc, i, last_find_partial = 0, we_cancel = 0;
  2047.     long        xsum;
  2048.     FOLDER_S       *new_f;
  2049.     MAILSTREAM       *create_stream;
  2050.     FSTATE_S        sub_state, *push_state;
  2051.     HelpType        help;
  2052.     static char        folder[MAXFOLDER+2];
  2053.     static ESCKEY_S subscribe_keys[] = {{ctrl('T'), 12, "^T", "To All Grps"},
  2054.                     {-1, 0, NULL, NULL}};
  2055.     extern long        line_hash();
  2056.  
  2057.     /*---- Build a context to find all news groups -----*/
  2058.     subscribe_cntxt         = *cntxt;
  2059.     subscribe_cntxt.use    |= CNTXT_FINDALL | CNTXT_NOFIND;
  2060.     subscribe_cntxt.use    &= ~CNTXT_PSEUDO;
  2061.     subscribe_cntxt.next    = NULL;
  2062.     subscribe_cntxt.folders = new_folder_list();
  2063.  
  2064.     /*
  2065.      * Prompt for group name.
  2066.      */
  2067.     add_folder  = &folder[1];            /* save position 0 */
  2068.     *add_folder = '\0';
  2069.     help = NO_HELP;
  2070.     while(1){
  2071.     xsum = line_hash(add_folder);
  2072.         rc = optionally_enter(add_folder, quest_line, 0, MAXFOLDER, 1, 0,
  2073.                   SUBSCRIBE_PMT, subscribe_keys, help, 0);
  2074.     removing_trailing_white_space(add_folder);
  2075.     removing_leading_white_space(add_folder);
  2076.         if((rc == 0 && *add_folder) || rc == 12){
  2077.         we_cancel = busy_alarm(1, "Fetching newsgoup list", NULL, 0);
  2078.  
  2079.         if(last_find_partial){
  2080.         /* clean up any previous find results */
  2081.         free_folders_in_context(&subscribe_cntxt);
  2082.         last_find_partial = 0;
  2083.         }
  2084.  
  2085.         if(rc == 12){            /* list the whole enchilada */
  2086.         find_folders_in_context(NULL, &subscribe_cntxt, NULL);
  2087.         }
  2088.         else if(i = strlen(add_folder)){
  2089.         /* clean up after any previous find */
  2090.         folder[0]    = '*';        /* insert preceding '*' */
  2091.         add_folder[i]   = '*';        /* and append '*' */
  2092.         add_folder[i+1] = '\0';
  2093.         find_folders_in_context(NULL, &subscribe_cntxt, folder);
  2094.         add_folder[i] = '\0';
  2095.         }
  2096.         else{
  2097.         q_status_message(SM_ORDER, 0, 2,
  2098.            "No group substring to match! Use ^T to list all news groups.");
  2099.         continue;
  2100.         }
  2101.  
  2102.         /*
  2103.          * If we did a partial find on matches, then we faked a full
  2104.          * find which will cause this to just return.
  2105.          */
  2106.         if(i = folder_total(subscribe_cntxt.folders)){
  2107.         char *f;
  2108.  
  2109.         /*
  2110.          * fake that we've found everything there is to find...
  2111.          */
  2112.         subscribe_cntxt.use &= ~(CNTXT_NOFIND|CNTXT_PARTFIND);
  2113.         last_find_partial = 1;
  2114.  
  2115.         if(i == 1){
  2116.             f = folder_entry(0, subscribe_cntxt.folders)->name;
  2117.             if(!strcmp(f, add_folder)){
  2118.             rc = 1;            /* success! */
  2119.             break;
  2120.             }
  2121.             else{            /* else complete the group */
  2122.             strcpy(add_folder, f);
  2123.             continue;
  2124.             }
  2125.         }
  2126.         else if(xsum == line_hash(add_folder)){
  2127.             /*
  2128.              * See if there wasn't an exact match in the lot.
  2129.              */
  2130.             while(i-- > 0){
  2131.             f = folder_entry(i,subscribe_cntxt.folders)->name;
  2132.             if(!strcmp(f, add_folder)){
  2133.  
  2134.             }
  2135.             else
  2136.               f = NULL;
  2137.             }
  2138.  
  2139.             /* if so, then the user picked it from the list the
  2140.              * last time and didn't change it at the prompt.
  2141.              * Must mean they're accepting it...
  2142.              */
  2143.             if(f){
  2144.             rc = 1;            /* success! */
  2145.             break;
  2146.             }
  2147.         }
  2148.         }
  2149.         else{
  2150.         if(rc == 12)
  2151.           q_status_message(SM_ORDER | SM_DING, 3, 3,
  2152.                    "No groups to select from!");
  2153.         else
  2154.           q_status_message1(SM_ORDER, 3, 3,
  2155.               "News group \"%s\" didn't match any existing groups",
  2156.               add_folder);
  2157.  
  2158.         continue;
  2159.         }
  2160.  
  2161. #ifdef NEWBB
  2162.         /*----- build a context to find new news groups -------*/
  2163.         newbb_cntxt = subscribe_cntxt;
  2164.         newbb_cntxt.use |= CNTXT_NOFIND | CNTXT_NEWBB;
  2165.         newbb_cntxt.use &= ~CNTXT_PSEUDO;
  2166.         newbb_cntxt.next = NULL;
  2167.         newbb_cntxt.folders = new_folder_list(); /* Not realy used */
  2168.         newbb_folder_list = newbb_cntxt.folders;
  2169.         find_folders_in_context(NULL, &newbb_cntxt, NULL);
  2170. #endif
  2171.         /*----- Mark groups that are currently subscribed too ------*/
  2172.         /* but first make sure they're found */
  2173.         find_folders_in_context(NULL, cntxt, NULL);
  2174.         for(i = 0 ; i < folder_total(subscribe_cntxt.folders); i++) {
  2175.         dprint(9, (debugfile, "PREFIX: \"%s\", %s\n",
  2176.                folder_entry(i,subscribe_cntxt.folders)->prefix,
  2177.                folder_entry(i,subscribe_cntxt.folders)->name));
  2178.         if(search_folder_list(cntxt->folders,
  2179.                    folder_entry(i,subscribe_cntxt.folders)->name))
  2180.           strcpy(folder_entry(i, subscribe_cntxt.folders)->prefix,
  2181.              "SUB ");
  2182.         else
  2183.           if(strlen(folder_entry(i,
  2184.                      subscribe_cntxt.folders)->prefix)!= 4)
  2185.             strcpy(folder_entry(i,subscribe_cntxt.folders)->prefix,
  2186.                "    ");
  2187.         }
  2188. #ifdef NEWBB
  2189.         /*-- Get the newly created groups to the top of the list --*/
  2190.         sort_folder_list(subscribe_cntxt.folders, compare_folders_new);
  2191. #endif
  2192.  
  2193.         if(we_cancel)
  2194.           cancel_busy_alarm(-1);
  2195.  
  2196.         /*----- Call the folder lister to do all the work -----*/
  2197.         push_state = fs;
  2198.         folders = NULL;
  2199.         rc = folder_lister(ps_global, Subscribe, &subscribe_cntxt,
  2200.                    NULL, add_folder, &folders, &subscribe_cntxt,
  2201.                    &sub_state);
  2202.         fs = push_state;
  2203.         redraw_folder_screen();
  2204.  
  2205.         if(rc <= 0){
  2206.         rc = -1;
  2207.         break;
  2208.         }
  2209.         else if(rc == 17 || F_ON(F_SELECT_WO_CONFIRM,ps_global))
  2210.           /*
  2211.            * The 17 comes from folder_lister, which returns 17 if it
  2212.            * passes back multiple groups in the folders array.
  2213.            */
  2214.           break;
  2215.  
  2216.     }
  2217.         else if(rc == 3){
  2218.             help = help == NO_HELP ? h_news_subscribe : NO_HELP;
  2219.     }
  2220.     else if(rc == 1 || add_folder[0] == '\0'){
  2221.         rc = -1;
  2222.         break;
  2223.     }
  2224.     }
  2225.  
  2226.     free_folder_list(&subscribe_cntxt.folders);
  2227.  
  2228.     if(rc < 0){
  2229.     if(rc == -1)
  2230.       q_status_message(SM_ORDER, 0, 3, "Subscribe cancelled");
  2231.  
  2232.     return(NULL);
  2233.     }
  2234.  
  2235.     /*------ Actually do the subscription -----*/
  2236.     if(rc == 17){
  2237.     int i, n = 0, errors = 0;
  2238.  
  2239.     /* subscribe one at a time */
  2240.     for(i=0; folders[i]; i++){
  2241.         context_apply(tmp_20k_buf, subscribe_cntxt.context, folders[i]);
  2242.         full_name = cpystr(tmp_20k_buf+1);
  2243.         rc = (int)mail_subscribe_bboard(NULL, full_name);
  2244.         fs_give((void **)&full_name);
  2245.         if(rc == 0){
  2246.         /*
  2247.          * This message may not make it to the screen, because
  2248.          * a c-client message about the failure will be there.
  2249.          * Probably best not to string together a whole bunch of errors
  2250.          * if there is something wrong.
  2251.          */
  2252.         q_status_message1(errors?SM_INFO:SM_ORDER, errors ? 0 : 3, 3,
  2253.                   "Error subscribing to \"%s\"", folders[i]);
  2254.         errors++;
  2255.         }
  2256.         else{
  2257.         /*
  2258.          * Save for updating cursor on display.  Arbitrarily choose
  2259.          * the first one in the list for the cursor location.
  2260.          */
  2261.         if(n == 0)
  2262.           strcpy(add_folder, folders[i]);
  2263.  
  2264.         n++;
  2265.         /*---- Update the screen display data structures -----*/
  2266.         if(ALL_FOUND(cntxt)){  /* Not sure what this does... */
  2267.             if(cntxt->use & CNTXT_PSEUDO){
  2268.             folder_delete(0, cntxt->folders);
  2269.             cntxt->use &= ~CNTXT_PSEUDO;
  2270.             }
  2271.  
  2272.             folder_insert(-1, new_folder(folders[i]), cntxt->folders);
  2273.         }
  2274.         }
  2275.     }
  2276.  
  2277.     if(n == 0){
  2278.         q_status_message(SM_ORDER | SM_DING, 3, 5,
  2279.         "Subscriptions failed, subscribed to no new groups");
  2280.         add_folder = NULL;
  2281.     }
  2282.     else
  2283.       q_status_message3(SM_ORDER | (errors ? SM_DING : 0), errors ? 3 : 0,3,
  2284.           "Subscribed to %s new groups%s%s",
  2285.           comatose((long)n),
  2286.           errors ? ", failed on " : "",
  2287.           errors ? comatose((long)errors) : "");
  2288.  
  2289.     for(i=0; folders[i]; i++)
  2290.       fs_give((void **)&folders[i]);
  2291.  
  2292.     fs_give((void **)&folders);
  2293.     }
  2294.     else{
  2295.     context_apply(tmp_20k_buf, subscribe_cntxt.context, add_folder);
  2296.     full_name = cpystr(tmp_20k_buf+1);
  2297.     rc = (int)mail_subscribe_bboard(NULL, full_name);
  2298.     fs_give((void **)&full_name);
  2299.     if(rc == 0){
  2300.         q_status_message1(SM_ORDER | SM_DING, 3, 3,
  2301.                   "Error subscribing to \"%s\"", add_folder);
  2302.         return(NULL);
  2303.     }
  2304.  
  2305.     /*---- Update the screen display data structures -----*/
  2306.     if(ALL_FOUND(cntxt)){  /* Not sure what this does... */
  2307.         if(cntxt->use & CNTXT_PSEUDO){
  2308.         folder_delete(0, cntxt->folders);
  2309.         cntxt->use &= ~CNTXT_PSEUDO;
  2310.         }
  2311.  
  2312.         folder_insert(-1, new_folder(add_folder), cntxt->folders);
  2313.     }
  2314.  
  2315.     q_status_message1(SM_ORDER, 0, 3, "Subscribed to \"%s\"", add_folder);
  2316.     }
  2317.  
  2318.     return(add_folder);
  2319. }
  2320.  
  2321.  
  2322.  
  2323. /*----------------------------------------------------------------------
  2324.       Rename folder
  2325.   
  2326.    Args: q_line     -- Screen line to prompt on
  2327.          index      -- index of folder in folder list to rename
  2328.          cntxt      -- collection of folders making up folder list
  2329.  
  2330.  Result: returns the new name of the folder, or NULL if nothing happened.
  2331.  
  2332.  When either the sent-mail or saved-message folders are renamed, immediately 
  2333. create a new one in their place so they always exist. The main loop above also
  2334. detects this and makes the rename look like an add of the sent-mail or
  2335. saved-messages folder. (This behavior may not be optimal, but it keeps things
  2336. consistent.
  2337.  
  2338.   ----*/
  2339. char *
  2340. rename_folder(q_line, index, cntxt)
  2341.      int        q_line, index;
  2342.      CONTEXT_S *cntxt;
  2343. {
  2344.     static char  new_foldername[MAXFOLDER+1];
  2345.     char        *folder, *prompt;
  2346.     HelpType     help;
  2347.     int          rc, ren_cur, cnt = 0;
  2348.     FOLDER_S    *new_f;
  2349.     MAILSTREAM  *ren_stream = NULL;
  2350.  
  2351.     dprint(4, (debugfile, "\n - rename folder -\n"));
  2352.  
  2353.     if(cntxt->type & FTYPE_BBOARD){
  2354.     q_status_message(SM_ORDER | SM_DING, 3, 3,
  2355.              "Can't rename bulletin boards or news groups!");
  2356.     return(NULL);
  2357.     }
  2358.     else if(!ALL_FOUND(cntxt) || (cntxt->use & CNTXT_PSEUDO)){
  2359.     q_status_message1(SM_ORDER | SM_DING, 0, 3,
  2360.               "No folder selected to rename.  %s list.",
  2361.               ALL_FOUND(cntxt) ? "Empty" : "Expand");
  2362.     return(NULL);
  2363.     }
  2364.     else if((new_f = folder_entry(index, cntxt->folders))
  2365.         && strucmp(FLDR_NAME(new_f), ps_global->inbox_name) == 0) {
  2366.         q_status_message1(SM_ORDER | SM_DING, 3, 4,
  2367.               "Can't change special folder name \"%s\"",
  2368.               ps_global->inbox_name);
  2369.         return(NULL);
  2370.     }
  2371.     else if(new_f->nickname){ 
  2372.     q_status_message(SM_ORDER | SM_DING, 3, 3,
  2373.              "Can't rename folder nicknames at this time!");
  2374.     return(NULL);
  2375.     }
  2376.  
  2377.     folder  = new_f->name;
  2378.     ren_cur = strcmp(folder, ps_global->cur_folder) == 0;
  2379.  
  2380.     prompt = "Rename folder to : ";
  2381.     help   = NO_HELP;
  2382.     strcpy(new_foldername, folder);
  2383.     while(1) {
  2384.         rc = optionally_enter(new_foldername, q_line, 0, MAXFOLDER, 1, 0,
  2385.                               prompt, NULL, help, 0);
  2386.         if(rc == 3) {
  2387.             help = help == NO_HELP ? h_oe_foldrename : NO_HELP;
  2388.             continue;
  2389.         }
  2390.  
  2391.     removing_trailing_white_space(new_foldername);
  2392.     removing_leading_white_space(new_foldername);
  2393.  
  2394.         if(rc == 0 && *new_foldername) {
  2395.         /* verify characters */
  2396.         if(!ps_global->show_dot_names && *new_foldername == '.'){
  2397.         if(cnt++ <= 0)
  2398.                   q_status_message(SM_ORDER,3,3,
  2399.             "Folder name can't begin with dot");
  2400.         else{
  2401.             NAMEVAL_S *feat;
  2402.             int i;
  2403.  
  2404.             for(i=0; (feat=feature_list(i))
  2405.                 && (feat->value != F_ENABLE_DOT_FOLDERS); i++)
  2406.               ;/* do nothing */
  2407.  
  2408.             q_status_message1(SM_ORDER,3,3,
  2409.               "Config feature \"%s\" enables names beginning with dot",
  2410.               feat && feat->name ? feat->name : "");
  2411.         }
  2412.  
  2413.                 display_message(NO_OP_COMMAND);
  2414.                 continue;
  2415.         }
  2416.  
  2417.         if(folder_index(new_foldername, cntxt->folders) >= 0){
  2418.                 q_status_message1(SM_ORDER,3,3, "Folder \"%s\" already exists",
  2419.                                   pretty_fn(new_foldername));
  2420.                 display_message(NO_OP_COMMAND);
  2421.                 continue;
  2422.             }
  2423.         }
  2424.  
  2425.         if(rc != 4) /* redraw */
  2426.           break;  /* no redraw */
  2427.  
  2428.     }
  2429.  
  2430.     if(rc==1 || new_foldername[0]=='\0' || strcmp(new_foldername, folder)==0){
  2431.         q_status_message(SM_ORDER, 0, 2, "Folder rename cancelled");
  2432.         return(0);
  2433.     }
  2434.  
  2435.     if(ren_cur && ps_global->mail_stream != NULL) {
  2436.         mail_close(ps_global->mail_stream);
  2437.         ps_global->mail_stream = NULL;
  2438.     }
  2439.  
  2440.     ren_stream = context_same_stream(cntxt->context, new_foldername,
  2441.                      ps_global->mail_stream);
  2442.  
  2443.     if(!ren_stream && ps_global->mail_stream != ps_global->inbox_stream)
  2444.       ren_stream = context_same_stream(cntxt->context, new_foldername,
  2445.                        ps_global->inbox_stream);
  2446.       
  2447.     if(rc = context_rename(cntxt->context,ren_stream,folder,new_foldername)){
  2448.     /* insert new name */
  2449.     new_f               = new_folder(new_foldername);
  2450.     new_f->prefix[0]    = '\0';
  2451.     new_f->msg_count    = 0;
  2452.     new_f->unread_count = 0;
  2453.     folder_insert(-1, new_f, cntxt->folders);
  2454.  
  2455.     if(strcmp(ps_global->VAR_DEFAULT_FCC, folder) == 0
  2456.        || strcmp(ps_global->VAR_DEFAULT_SAVE_FOLDER, folder) == 0) {
  2457.         /* renaming sent-mail or saved-messages */
  2458.         if(context_create(cntxt->context, NULL, folder)){
  2459.         q_status_message3(SM_ORDER,0,3,
  2460.              "Folder \"%s\" renamed to \"%s\". New \"%s\" created",
  2461.                   folder, new_foldername,
  2462.                   pretty_fn(
  2463.                     (strcmp(ps_global->VAR_DEFAULT_SAVE_FOLDER,
  2464.                         folder) == 0)
  2465.                     ? ps_global->VAR_DEFAULT_SAVE_FOLDER
  2466.                     : ps_global->VAR_DEFAULT_FCC));
  2467.  
  2468.         }
  2469.         else{
  2470.         if((index = folder_index(folder, cntxt->folders)) >= 0)
  2471.           folder_delete(index, cntxt->folders); /* delete old struct */
  2472.  
  2473.         q_status_message1(SM_ORDER | SM_DING, 3, 4,
  2474.                   "Error creating new \"%s\"", folder);
  2475.  
  2476.         dprint(2, (debugfile, "Error creating \"%s\" in %s context\n",
  2477.                folder, cntxt->context));
  2478.         }
  2479.     }
  2480.     else{
  2481.         q_status_message2(SM_ORDER, 0, 3, "Folder \"%s\" renamed to \"%s\"",
  2482.                   pretty_fn(folder), pretty_fn(new_foldername));
  2483.  
  2484.         if((index = folder_index(folder, cntxt->folders)) >= 0)
  2485.           folder_delete(index, cntxt->folders); /* delete old struct */
  2486.     }
  2487.     }
  2488.  
  2489.     if(ren_cur) {
  2490.         /* No reopen the folder we just had open */
  2491.         do_broach_folder(new_foldername, cntxt);
  2492.     }
  2493.  
  2494.     return(rc ? new_foldername : NULL);
  2495. }
  2496.  
  2497.  
  2498.  
  2499. /*----------------------------------------------------------------------
  2500.    Confirm and delete a folder
  2501.  
  2502.    Args: index -- Index of folder in collection to remove
  2503.          cntxt -- The particular collection the folder's to be remove from
  2504.          mangled_header -- Pointer to flag to set if the the anchor line
  2505.                            needs updating (deleted the open folder)
  2506.  
  2507.  Result: return 0 if not delete, 1 if deleted.
  2508.  
  2509.  NOTE: Currently disallows deleting open folder...
  2510.   ----*/
  2511. int
  2512. delete_folder(index, cntxt, mangled_header)
  2513.     int        index, *mangled_header;
  2514.     CONTEXT_S *cntxt;
  2515. {
  2516.     char       *folder, *full_folder, ques_buf[MAX_SCREEN_COLS+1];
  2517.     MAILSTREAM *del_stream = NULL;
  2518.     FOLDER_S   *fp;
  2519.     int         ret, close_opened = 0;
  2520.  
  2521.     if(cntxt->type & FTYPE_BBOARD){
  2522.     static char fmt[] = "Really unsubscribe from \"%.*s\"";
  2523.          
  2524.         folder = folder_entry(index, cntxt->folders)->name;
  2525.     /* 4 is strlen("%.*s") */
  2526.         sprintf(ques_buf, fmt, sizeof(ques_buf) - (sizeof(fmt)-4), folder);
  2527.     
  2528.         ret = want_to(ques_buf, 'n', 'x', NO_HELP, 0, 0);
  2529.         switch(ret) {
  2530.           /* ^C */
  2531.           case 'x':
  2532.             Writechar(BELL, 0);
  2533.             /* fall through */
  2534.           case 'n':
  2535.             return(0);
  2536.         }
  2537.     
  2538.         dprint(2, (debugfile, "deleting folder \"%s\" in context \"%s\"\n",
  2539.            folder, cntxt->context));
  2540.  
  2541.         context_apply(tmp_20k_buf, cntxt->context, folder);
  2542.     full_folder = cpystr(tmp_20k_buf + 1);
  2543.     ret = (int)mail_unsubscribe_bboard(NULL, full_folder);
  2544.     fs_give((void **)&full_folder);
  2545.         if(ret == 0){
  2546.             q_status_message1(SM_ORDER | SM_DING, 3, 3,
  2547.                   "Error unsubscribing from \"%s\"", folder);
  2548.             return(0);
  2549.         }
  2550.  
  2551.     return(1);
  2552.     }
  2553.  
  2554.     if(!folder_total(cntxt->folders)){
  2555.     q_status_message(SM_ORDER | SM_DING, 0, 4,
  2556.              "Empty folder collection.  No folder to delete!");
  2557.     return(0);
  2558.     }
  2559.  
  2560.     if(cntxt->use & CNTXT_INCMNG){
  2561.     if(ps_global->readonly_pinerc){
  2562.         q_status_message(SM_ORDER,3,5,
  2563.         "Deletion cancelled: config file not editable");
  2564.         return(0);
  2565.     }
  2566.     }
  2567.  
  2568.     fp     = folder_entry(index, cntxt->folders);
  2569.     folder = FLDR_NAME(fp);
  2570.     dprint(4, (debugfile, "=== delete_folder(%s) ===\n", folder));
  2571.  
  2572.     if(strucmp(folder, ps_global->inbox_name) == 0) {
  2573.     q_status_message1(SM_ORDER | SM_DING, 3, 4,
  2574.          "Can't delete special folder \"%s\".", ps_global->inbox_name);
  2575.     return(0);
  2576.     }
  2577.     else if(cntxt == ps_global->context_current
  2578.         && strcmp(folder, ps_global->cur_folder) == 0)
  2579.       close_opened++;
  2580.  
  2581.     sprintf(ques_buf, "Really delete \"%s\"%s", folder, 
  2582.         close_opened ? " (the currently open folder)" : "");
  2583.  
  2584.     if((ret=want_to(ques_buf, 'n', 'x', NO_HELP, 0, 0)) != 'y'){
  2585.     q_status_message(SM_ORDER,0,3, (ret == 'x') ? "Delete cancelled" 
  2586.                           : "No folder deleted");
  2587.     return(0);
  2588.     }
  2589.  
  2590.     dprint(2, (debugfile, "deleting folder \"%s\" (%s) in context \"%s\"\n",
  2591.            fp->name, fp->nickname ? fp->nickname : "", cntxt->context));
  2592.  
  2593.     /*
  2594.      * Use fp->name since "folder" may be a nickname...
  2595.      */
  2596.     if(close_opened){
  2597.     /*
  2598.      * There *better* be a stream, but check just in case.  Then
  2599.      * close it, NULL the pointer, and let do_broach_folder fixup
  2600.      * the rest...
  2601.      */
  2602.     if(ps_global->mail_stream){
  2603.         mail_close(ps_global->mail_stream);
  2604.         ps_global->mail_stream = NULL;
  2605.         *mangled_header = 1;
  2606.         do_broach_folder(ps_global->inbox_name, ps_global->context_list);
  2607.     }
  2608.     }
  2609.     else
  2610.       del_stream = context_same_stream(cntxt->context, fp->name,
  2611.                        ps_global->mail_stream);
  2612.  
  2613.     if(!del_stream && ps_global->mail_stream != ps_global->inbox_stream)
  2614.       del_stream = context_same_stream(cntxt->context, fp->name,
  2615.                        ps_global->inbox_stream);
  2616.  
  2617.     if(!context_delete(cntxt->context, del_stream, fp->name)){
  2618. /*
  2619.  * BUG: what if sent-mail or saved-messages????
  2620.  */
  2621.     q_status_message1(SM_ORDER,3,3,"Delete of \"%s\" Failed!", folder);
  2622.     return(0);
  2623.     }
  2624.  
  2625.     if(cntxt->use & CNTXT_INCMNG){
  2626.     char *p;
  2627.     int   i;
  2628.     for(i = 0; ps_global->USR_INCOMING_FOLDERS[i]; i++){
  2629.         /*
  2630.          * Quite a test, non?
  2631.          */
  2632.         if((p = srchstr(ps_global->USR_INCOMING_FOLDERS[i], fp->name))
  2633.            && p[strlen(fp->name)] == '\0'
  2634.            && (p == ps_global->USR_INCOMING_FOLDERS[i]
  2635.            || isspace(*(p-1)))){
  2636.         fs_give((void **)&(ps_global->USR_INCOMING_FOLDERS[i]));
  2637.         while(ps_global->USR_INCOMING_FOLDERS[i]
  2638.               = ps_global->USR_INCOMING_FOLDERS[i+1])
  2639.           i++;                /* rub it out */
  2640.         }
  2641.     }
  2642.  
  2643.     write_pinerc(ps_global);        /* save new element */
  2644.     }
  2645.  
  2646.     q_status_message1(SM_ORDER, 0,3,"Folder \"%s\" deleted!", folder);
  2647.     return(1);
  2648. }
  2649.  
  2650.  
  2651.  
  2652. /*----------------------------------------------------------------------
  2653.       Print the list of folders on paper
  2654.  
  2655.    Args: list    --  The current list of folders
  2656.          lens    --  The list of lengths of the current folders
  2657.          display --  The current folder display structure
  2658.  
  2659.  Result: list printed on paper
  2660.  
  2661. If the display list was created for 80 columns it is used, otherwise
  2662. a new list is created for 80 columns
  2663.  
  2664.   ----*/
  2665.  
  2666. void
  2667. print_folders(display)
  2668.     FSTATE_S  *display;
  2669. {
  2670.     int i, index, l;
  2671.     CONTEXT_S *context;
  2672.     FOLDER_S  *f;
  2673.     char       buf[256];
  2674.  
  2675.     if(ps_global->ttyo->screen_cols != 80)
  2676.       create_folder_display(display, 80);
  2677.  
  2678.     if(open_printer("folder list ") != 0)
  2679.       return;
  2680.  
  2681.     context = display->context_list;
  2682.     index   = i = 0;
  2683.     while(context){
  2684.     for(;i < context->d_line;i++) /* leading spaces */
  2685.       print_text(NEWLINE);
  2686.  
  2687.     memset((void *)tmp_20k_buf,LINECH, 80 * sizeof(char));
  2688.     tmp_20k_buf[80] = '\0';
  2689.     print_text(tmp_20k_buf);
  2690.     print_text(NEWLINE);
  2691.     i++;
  2692.  
  2693.     memset((void *)tmp_20k_buf,' ', 80 * sizeof(char));
  2694.     tmp_20k_buf[80] = '\0';
  2695.     if(context->use & CNTXT_INCMNG){
  2696.         i = strlen(context->label[0]);
  2697.         strncpy(tmp_20k_buf + max(40 - (i/2), 0), context->label[0], i);
  2698.     }
  2699.     else{
  2700.         sprintf(tmp_20k_buf + 80 + 2, 
  2701.             " %s-collection <%s>  %s", 
  2702.             (context->type & FTYPE_BBOARD) ? "News" : "Mail",
  2703.             context->label[0], 
  2704.             (context->use & CNTXT_SAVEDFLT)
  2705.                            ? "** Default for Saves **" : "");
  2706.         strncpy(tmp_20k_buf, 
  2707.             tmp_20k_buf + 80 + 2, 
  2708.             strlen(tmp_20k_buf + 80 + 2));
  2709.         strncpy(tmp_20k_buf + 80
  2710.                        - ((context->type & FTYPE_REMOTE) ? 9 : 8),
  2711.             (context->type & FTYPE_REMOTE)?"(Remote)":"(Local)",
  2712.             (context->type & FTYPE_REMOTE) ? 8 : 7);
  2713.     }
  2714.         print_text(tmp_20k_buf);
  2715.         print_text(NEWLINE);
  2716.         i++;
  2717.  
  2718.     memset((void *)tmp_20k_buf,LINECH,80 * sizeof(char));
  2719.     tmp_20k_buf[80] = '\0';
  2720.     print_text(tmp_20k_buf);
  2721.     print_text(NEWLINE);
  2722.     i++;
  2723.  
  2724.     for(i++; folder_total(context->folders) 
  2725.         && i < (int)folder_entry(0, context->folders)->d_line ; i++)
  2726.       print_text(NEWLINE);
  2727.  
  2728.     *tmp_20k_buf = '\0';
  2729.     for(index=0; index < folder_total(context->folders); index++){
  2730.         f = folder_entry(index, context->folders);
  2731.         if(f->d_col == 0){
  2732.         i++;
  2733.         strcat(tmp_20k_buf, NEWLINE);
  2734.         print_text(tmp_20k_buf);
  2735.         tmp_20k_buf[0] = '\0';
  2736.         }
  2737.  
  2738.         l = strlen(tmp_20k_buf);
  2739.         if(context->type & FTYPE_BBOARD)
  2740.           sprintf(buf, "%*s%s", 
  2741.               strlen(f->prefix) + max(0,((int)f->d_col - l)),
  2742.               f->prefix,
  2743.               f->name);
  2744.         else
  2745.           sprintf(buf, "%*s", f->name_len + max(0,((int)f->d_col - l)),
  2746.               FLDR_NAME(f));
  2747.         strcat(tmp_20k_buf, buf);
  2748.     }
  2749.  
  2750.     print_text(tmp_20k_buf);
  2751.     print_text(NEWLINE);
  2752.     context = context->next;
  2753.     }
  2754.  
  2755.     close_printer();
  2756.     if(ps_global->ttyo->screen_cols != 80)
  2757.       create_folder_display(display, ps_global->ttyo->screen_cols);
  2758. }
  2759.  
  2760.                      
  2761.  
  2762. /*----------------------------------------------------------------------
  2763.   Search folder list
  2764.  
  2765.    Args: fd       -- The folder display structure
  2766.          index    -- pointer to index of current folder (new folder if found)
  2767.          context  -- pointer to context of current folder (new folder if found)
  2768.          ask_line -- Screen line to prompt on
  2769.  
  2770.  Result: returns 
  2771.                 -1 if aborted
  2772.                  0 if NOT found
  2773.          1 if found
  2774.          2 if found and wrapped
  2775.   ----------------------------------------------------------------------*/
  2776. int
  2777. search_folders(fd, ask_line)
  2778.     FSTATE_S   *fd;
  2779.     int         ask_line;
  2780. {
  2781.     char            prompt[MAX_SEARCH+50], nsearch_string[MAX_SEARCH+1];
  2782.     HelpType        help = NO_HELP;
  2783.     CONTEXT_S      *t_context;
  2784.     FOLDER_S       *f;
  2785.     int             rc, t_index;
  2786.     static char     search_string[MAX_SEARCH+1];
  2787.     static ESCKEY_S folder_search_keys[] = { { 0, 0, "", "" },
  2788.                         {ctrl('Y'), 10, "^Y", "First Fldr"},
  2789.                         {ctrl('V'), 11, "^V", "Last Fldr"},
  2790.                         {-1, 0, NULL, NULL} };
  2791.  
  2792.     nsearch_string[0] = '\0';
  2793.     if(!folder_total((t_context = fd->context)->folders)){
  2794.     q_status_message(SM_ORDER | SM_DING, 0, 4,
  2795.              "Empty folder collection.  No folders to search!");
  2796.     return(0);
  2797.     }
  2798.  
  2799.     t_index           = fd->folder_index;
  2800.     sprintf(prompt, "Folder name to search for %s%s%s: ", 
  2801.         (*search_string == '\0') ? "" : "[", 
  2802.         search_string,
  2803.         (*search_string == '\0') ? "" : "] ");
  2804.  
  2805.     while(1) {
  2806.         rc = optionally_enter(nsearch_string, ask_line, 0, MAX_SEARCH, 1,
  2807.                   0, prompt, folder_search_keys, help,0);
  2808.         if(rc == 3) {
  2809.             help = help == NO_HELP ? h_oe_foldsearch : NO_HELP;
  2810.             continue;
  2811.         }
  2812.     else if(rc == 10){
  2813.         fd->context      = fd->context_list;
  2814.         fd->folder_index = 0;
  2815.         q_status_message(SM_ORDER, 0, 3, "Searched to First Folder.");
  2816.         return(3);
  2817.     }
  2818.     else if(rc == 11){
  2819.         for(fd->context = fd->context_list;
  2820.         fd->context->next; 
  2821.         fd->context = fd->context->next)
  2822.           ;
  2823.  
  2824.         fd->folder_index = (ALL_FOUND(fd->context)) 
  2825.                 ? folder_total(fd->context->folders) - 1: 0;
  2826.         q_status_message(SM_ORDER, 0, 3, "Searched to Last Folder.");
  2827.         return(3);
  2828.     }
  2829.  
  2830.         if(rc != 4)
  2831.           break;
  2832.     }
  2833.  
  2834.     if(rc == 1 || (search_string[0] == '\0' && nsearch_string[0] == '\0'))
  2835.       return(-1);            /* abort */
  2836.  
  2837.     if(nsearch_string[0] != '\0')
  2838.       strcpy(search_string, nsearch_string);
  2839.  
  2840.     /*----- Search the bottom half of list ------*/
  2841.     rc = 0;
  2842.     while(1){
  2843.     if(t_index + 1 >= folder_total(t_context->folders)){
  2844.         t_index = 0;
  2845.         if(!(t_context = t_context->next)){
  2846.         t_context = fd->context_list;     /* wrap the search */
  2847.         rc = 1;
  2848.         }
  2849.     }
  2850.     else
  2851.       t_index++;
  2852.  
  2853.     if(t_index == fd->folder_index && t_context == fd->context)
  2854.       return(0);
  2855.  
  2856.     f = folder_entry(t_index, t_context->folders);
  2857.         if(srchstr(FLDR_NAME(f), search_string)){
  2858.         fd->folder_index   = t_index;
  2859.         fd->context        = t_context;
  2860.         return(rc + 1);
  2861.     }
  2862.     }
  2863. }
  2864.  
  2865. #ifdef NEWBB
  2866. /*----------------------------------------------------------------------
  2867.   Clears the "NEW " prefix off all the news groups that have it and
  2868. sorts them back into their normal positions in the list.  Also, resets
  2869. the time to check new groups against.
  2870.  
  2871. Args: flist  -- The folder list display structure to clear and sort
  2872.  
  2873. Returns: nothing
  2874.  
  2875. Using ctime format for the last time checked is OK, Probably should
  2876. use RFC-822 date format.  The important thing is that it is ASCII and
  2877. that code exists to parse it and convert it to the correct format.
  2878.   ----*/
  2879. void
  2880. clear_new_groups(flist)
  2881.      void *flist;
  2882. {
  2883.     int i;
  2884.     long now;
  2885.  
  2886.  
  2887.     /*------ Set the time to check for new groups with to now -----*/
  2888.     now    = time(0);
  2889.     set_variable(V_NNTP_NEW_GROUP_TIME, ctime(&now), 0);
  2890.  
  2891.     /*---- Change the "NEW " prefixes in the folder list to "    " -----*/
  2892.     for(i = 0; i < folder_total(flist); i++) {
  2893.         if(strcmp(folder_entry(i, flist)->prefix, "NEW ") == 0) {
  2894.             strcpy(folder_entry(i, flist)->prefix, "    ");
  2895.         } else {
  2896.             /* Done cause all new ones are at the top */
  2897.             break;
  2898.         }
  2899.     }
  2900.  
  2901.     /*--- Put the list in order, those that were new no longer at top ---*/
  2902.     sort_folder_list(flist, compare_folders);
  2903. }
  2904.  
  2905.  
  2906.  
  2907. /*----------------------------------------------------------------------
  2908.    Convert a date in ctime(3) format to the format required by NNTP
  2909.   NEWGROUP command (YYMMDD HHMMSS). 
  2910.  
  2911. Args: date -- Date string in ctime format 
  2912.        
  2913. Returns: date in NNTP format in static buffer 
  2914.   ----*/
  2915. char *
  2916. ctime2nntp(date)
  2917.      char *date;
  2918. {
  2919.     struct date d;
  2920.     static char timebuf[40];
  2921.  
  2922.     parse_date(date, &d);
  2923.  
  2924.     sprintf(timebuf, "%02d%02d%02d %02d%02d%02d",
  2925.             d.year % 100, d.month, d.day, d.hour, d.minute, d.sec);
  2926.     return(timebuf);
  2927. }
  2928. #endif    
  2929.  
  2930.  
  2931. /*----------------------------------------------------------------------
  2932.       compare two names for qsort, case independent
  2933.  
  2934.    Args: pointers to strings to compare
  2935.  
  2936.  Result: integer result of strcmp of the names.  Uses simple 
  2937.          efficiency hack to speed the string comparisons up a bit.
  2938.  
  2939.   ----------------------------------------------------------------------*/
  2940. int
  2941. compare_names(x, y)
  2942.     const QSType *x, *y;
  2943. {
  2944.     char *a = *(char **)x, *b = *(char **)y;
  2945.     int r;
  2946. #if defined(DOS) || defined(OS2)
  2947. #define    STRCMP    strucmp
  2948. #define    CMPI    UCMPI
  2949. #else
  2950. #define    STRCMP    strcmp
  2951. #define    CMPI(X,Y)    ((X)[0] - (Y)[0])
  2952. #endif
  2953. #define    UCMPI(X,Y)    ((isupper((X)[0]) ? (X)[0] - 'A' + 'a' : (X)[0])  \
  2954.              - (isupper((Y)[0]) ? (Y)[0] - 'A' + 'a' : (Y)[0]))
  2955.  
  2956.     /*---- Inbox always sorts to the top ----*/
  2957.     if((UCMPI(b, ps_global->inbox_name)) == 0
  2958.        && strucmp(b, ps_global->inbox_name) == 0)
  2959.       return((CMPI(a, b) == 0 && STRCMP(a, b) == 0) ? 0 : 1);
  2960.     else if(CMPI(b, ps_global->VAR_DEFAULT_FCC) == 0
  2961.         && STRCMP(b, ps_global->VAR_DEFAULT_FCC) == 0)
  2962.       return((CMPI(a, b) == 0 && STRCMP(a, b) == 0) ? 0 : 1);
  2963.     else if(CMPI(b, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0
  2964.         && STRCMP(b, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0)
  2965.       return((CMPI(a, b) == 0 && STRCMP(a, b) == 0) ? 0 : 1);
  2966.     else if(UCMPI(a, ps_global->inbox_name) == 0
  2967.         && strucmp(a, ps_global->inbox_name) == 0)
  2968.       return((CMPI(a, b) == 0 && STRCMP(a, b) == 0) ? 0 : -1);
  2969.     /*----- The sent-mail folder, is always next ---*/
  2970.     else if(CMPI(a, ps_global->VAR_DEFAULT_FCC) == 0
  2971.         && STRCMP(a, ps_global->VAR_DEFAULT_FCC) == 0)
  2972.       return((CMPI(a, b) == 0 && STRCMP(a, b) == 0) ? 0 : -1);
  2973.     /*----- The saved-messages folder, is always next ---*/
  2974.     else if(CMPI(a, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0
  2975.         && STRCMP(a, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0)
  2976.       return((CMPI(a, b) == 0 && STRCMP(a, b) == 0) ? 0 : -1);
  2977.     else
  2978.       return((r = CMPI(a, b)) ? r : STRCMP(a, b));
  2979. }
  2980.  
  2981.  
  2982.  
  2983. /*----------------------------------------------------------------------
  2984.   This code calculate the screen arrangement for the folders screen.
  2985. It fills in the line and col number for each entry in the list.
  2986.  
  2987. Args: f_list  -- The folder list
  2988.       f_list_size -- The number of entries in the folder list 
  2989.       screen_cols -- The number of columns on the screen
  2990.  
  2991.  
  2992. BUG - test this with one, two and three folders
  2993. Returns: The folder display structure
  2994.   ----*/
  2995.  
  2996. compare_sizes(f1, f2)
  2997.      const QSType *f1, *f2;
  2998. {
  2999.     return((*(struct folder **)f1)->name_len - 
  3000.            (*(struct folder **)f2)->name_len);
  3001. }
  3002.  
  3003. compare_folders(f1, f2)
  3004.      const QSType *f1, *f2;
  3005. {
  3006.     char *s1, *s2;
  3007.  
  3008.     s1 = (*(FOLDER_S **)f1)->name;
  3009.     s2 = (*(FOLDER_S **)f2)->name;
  3010.  
  3011.     return(compare_names(&s1, &s2));
  3012. }
  3013.  
  3014. #ifdef NEWBB
  3015. /*----------------------------------------------------------------------
  3016.    Folder comparison that puts those with "NEW " prefix at top of list
  3017.   ---*/
  3018. compare_folders_new(f1, f2)
  3019.      const QSType *f1, *f2;
  3020. {
  3021.     char *s1, *s2;
  3022.     int   is_new1, is_new2;
  3023.  
  3024.     s1      = (*((FOLDER_S **)(f1)))->name;
  3025.     s2      = (*((FOLDER_S **)(f2)))->name;
  3026.     is_new1 = strcmp(((*(FOLDER_S **)(f1)))->prefix, "NEW ") == 0;
  3027.     is_new2 = strcmp(((*(FOLDER_S **)(f2)))->prefix, "NEW ") == 0;
  3028.  
  3029.     if(!(is_new1 ^ is_new2))
  3030.       return(compare_names(&s1, &s2));
  3031.     else if(is_new1)
  3032.       return(-11);
  3033.     else
  3034.       return(1);
  3035. }
  3036. #endif
  3037.  
  3038. /*----------------------------------------------------------------------
  3039.    Calculate the arrangement on the screen. This fills in the rwo and column
  3040.  in the struct folders in the global Pine folder list. 
  3041.  
  3042. Args: fold_disp  -- folder menu state 
  3043.       screen_cols -- The width of the screen
  3044.  
  3045. Names are passed pre-sorted.
  3046.   ---*/
  3047.  
  3048. void
  3049. create_folder_display(fold_disp, screen_cols)
  3050.      FSTATE_S *fold_disp;
  3051.      int       screen_cols;
  3052. {
  3053.     register int       index;
  3054.     register FOLDER_S *f;
  3055.     CONTEXT_S         *c_list;
  3056.     int                length = 0, row, col, goal;
  3057.  
  3058.     if(!fold_disp){            /* what? */
  3059.     q_status_message(SM_ORDER,3,3,
  3060.         "Programmer BOTCH: No folder state struct!");
  3061.     return;
  3062.     }
  3063.  
  3064.     row                      = (ps_global->context_list->next) ? 1 : 0;
  3065.     c_list                   = fold_disp->context_list;
  3066.     fold_disp->display_cols  = screen_cols;
  3067.     fold_disp->display_rows  = ps_global->ttyo->screen_rows 
  3068.                                  - FOOTER_ROWS(ps_global)
  3069.                                  - HEADER_ROWS(ps_global);
  3070.     while(c_list != NULL){
  3071.     /*--- 
  3072.       Figure out the column width to use for display. What we want is a 
  3073.       column width that looks nice for most of the folder names, but
  3074.       not to make the columns super wide because of a few folder names
  3075.       of exceptional length. This is done by sorting the lengths of the
  3076.       existing folders and using a width that will suit 95% of the 
  3077.       entries. 
  3078.  
  3079.       The miminum columns width used is 20.
  3080.      ----*/
  3081.     if(!ALL_FOUND(c_list)){
  3082.         length = strlen(CLICKHERE);
  3083.     }
  3084.     else if(!folder_total(c_list->folders) || (c_list->use&CNTXT_PSEUDO)){
  3085.         length = strlen(c_list->use&CNTXT_NEWS ? CLICKHERETOONEWS
  3086.                            : CLICKHERETOO);
  3087.     }
  3088.     else if(F_ON(F_VERT_FOLDER_LIST, ps_global)){
  3089.         length = screen_cols; /* not used */
  3090.     }
  3091.     else{
  3092.         length = 0;        /* start from scratch */
  3093.         for(index = 0; index < folder_total(c_list->folders); index++){
  3094.         f = folder_entry(index, c_list->folders);
  3095.         length = max(length, (int)f->name_len
  3096.                     + (f->prefix ? strlen(f->prefix) : 0)
  3097.                     + 1);
  3098.         }
  3099.     }
  3100.  
  3101.     length = max(20, length);
  3102.  
  3103.  
  3104.     /*---- 
  3105.       Now we go through the display list and fill in the rows and columns.
  3106.       If the entry is just a text entry then it is centered. If it is
  3107.       a folder name it is fit in after the preceed one if possible. If 
  3108.       not it is put on the line boundrieds
  3109.          ---*/
  3110.  
  3111.     if(ps_global->context_list->next){
  3112.         /* leave a line for each label... */
  3113.         for(index = 0; c_list->label[index] != NULL; index++){
  3114.         if(index == 0)
  3115.           c_list->d_line = row;    /* remember which row to start on */
  3116.  
  3117.         row++;
  3118.         }
  3119.  
  3120.         row++;            /* one blank line after labels */
  3121.         row++;            /* one more blank line after labels */
  3122.     }
  3123.  
  3124.     /* then assign positions for each folder name */
  3125.     col = 0;
  3126.     if(!ALL_FOUND(c_list)){
  3127.         if(c_list->use & CNTXT_PSEUDO){
  3128.         f = folder_entry(0, c_list->folders);
  3129.         }
  3130.         else{
  3131.         f = new_folder(CLICKHERE);
  3132.         folder_insert(0, f, c_list->folders);
  3133.         c_list->use |= CNTXT_PSEUDO;
  3134.         }
  3135.  
  3136.         f->d_line = row;
  3137.         f->d_col  = max(0,
  3138.                          (screen_cols - (int)f->name_len-strlen(f->prefix))/2);
  3139.     }
  3140.     else if(!folder_total(c_list->folders) || (c_list->use&CNTXT_PSEUDO)){
  3141.         if(c_list->use & CNTXT_PSEUDO)
  3142.           folder_delete(0, c_list->folders); /* may be CLICKHERE */
  3143.  
  3144.         f = new_folder(c_list->use&CNTXT_NEWS ? CLICKHERETOONEWS
  3145.                           : CLICKHERETOO);
  3146.         folder_insert(0, f, c_list->folders);
  3147.         c_list->use |= CNTXT_PSEUDO; /* let others know entry's bogus */
  3148.         f->d_line    = row;
  3149.         f->d_col     = max(0, (screen_cols - (int)f->name_len -
  3150.                                    strlen(f->prefix))/2);
  3151.     }
  3152.     /* one folder per line */
  3153.     else if(F_ON(F_VERT_FOLDER_LIST, ps_global)){
  3154.         for(index = 0; index < folder_total(c_list->folders); index++){
  3155.         f    = folder_entry(index, c_list->folders);
  3156.         if(index)
  3157.           row++;
  3158.  
  3159.         f->d_line = (unsigned int) row;
  3160.         f->d_col  = (unsigned int) col;
  3161.         }
  3162.     }
  3163.     else{
  3164.         int plen;
  3165.  
  3166.         for(index = 0; index < folder_total(c_list->folders); index++){
  3167.         f    = folder_entry(index, c_list->folders);
  3168.         plen = f->prefix ? strlen(f->prefix) : 0;
  3169.  
  3170.         if(col + (int)f->name_len + plen >= screen_cols){
  3171.             col = 0;
  3172.             row++;
  3173.         }
  3174.  
  3175.         f->d_line = (unsigned int) row;
  3176.         f->d_col  = (unsigned int) col;
  3177.  
  3178.         for(goal = 0; goal < (int) f->name_len + plen;
  3179.                     goal += length)
  3180.           col += length;
  3181.         }
  3182.     }
  3183.  
  3184.     fold_disp->last_row = row++;
  3185.     row++;                /* add a blank line */
  3186.     row++;                /* add another blank line */
  3187.  
  3188.     c_list = c_list->next;     /* format the next section... */
  3189.     }
  3190. }
  3191.  
  3192.  
  3193.  
  3194. /*----------------------------------------------------------------------
  3195.       Format the given folder name for display for the user
  3196.  
  3197.    Args: folder -- The folder name to fix up
  3198.  
  3199. Not sure this always makes it prettier. It could do nice truncation if we
  3200. passed in a length. Right now it adds the path name of the mail 
  3201. subdirectory if appropriate.
  3202.  ----*/
  3203.       
  3204. char *
  3205. pretty_fn(folder)
  3206.      char *folder;
  3207. {
  3208.     static char  pfn[MAXFOLDER * 2 + 1];
  3209.     char        *p;
  3210.  
  3211. #if defined(DOS) || defined(OS2)
  3212.     if(!ps_global->show_folders_dir || *folder == '\\' || 
  3213. #else
  3214.     if(!ps_global->show_folders_dir || *folder == '/' || *folder == '~' ||
  3215. #endif
  3216.        *folder == '{' || *folder == '\0' 
  3217.        || !strucmp(folder, ps_global->inbox_name))  {
  3218.         if(ps_global->nr_mode) {
  3219.             if((p = strindex(folder, '}')) != NULL)
  3220.               return(p +1);
  3221.             else if((p = strindex(folder, '/')) != NULL) 
  3222.               return(p+1);
  3223.         else
  3224.               return(folder);
  3225.         }
  3226.     else if(!strucmp(folder, ps_global->inbox_name)){
  3227.         strcpy(pfn, ps_global->inbox_name);
  3228.         return(pfn);
  3229.     }
  3230.     else
  3231.           return(folder);
  3232.  
  3233.     } else {
  3234.         build_path(pfn, ps_global->VAR_MAIL_DIRECTORY, folder);
  3235.         return(pfn);
  3236.     }
  3237. }
  3238.  
  3239.  
  3240.  
  3241. /*----------------------------------------------------------------------
  3242.        Check to see if folder exists in given context
  3243.  
  3244.   Args: c_string -- context to check for folder in
  3245.         file  -- name of folder to check
  3246.  
  3247.  Result: returns 1 if the folder exists
  3248.                  0 if not
  3249.         -1 on error
  3250.  
  3251.   Uses mail_find to sniff out the existance of the requested folder.
  3252.   The context string is just here for convenience.  Checking for
  3253.   folder's existance within a given context is probably more efficiently
  3254.   handled outside this function for now using find_folders_in_context().
  3255.  
  3256.     ----*/
  3257. int
  3258. folder_exists(c_string, file)
  3259.     char *c_string, *file;
  3260. {
  3261.     char         fn[MAXPATH+1], host[MAXPATH+1], mbox[MAXPATH+1], *find_string;
  3262.     int          ourstream = 0, old_dot_state, old_inbox_state, we_cancel = 0;
  3263.     MAILSTREAM  *find_stream = NULL;
  3264.  
  3265.     we_cancel = busy_alarm(1, NULL, NULL, 0);
  3266.  
  3267.     if(c_string && *c_string && context_isambig(file))
  3268.       context_apply(fn, c_string, file);
  3269.     else
  3270.       strcpy(fn, file);
  3271.  
  3272.     find_stream = mail_valid(NULL, fn, NULL) ? mail_open(NULL,fn,OP_PROTOTYPE)
  3273.                          : NULL;
  3274.     if(find_stream && mail_valid_net(fn, find_stream->dtb, host, mbox)){
  3275.     if(!(find_stream = same_stream(fn, ps_global->mail_stream))){
  3276.         if(!(find_stream = same_stream(fn, ps_global->inbox_stream))){
  3277.         char tmp[MAXPATH], options[MAXPATH], *p = fn, *p2;
  3278.         while(*p && *p != '}' && *p != '/')
  3279.           p++;
  3280.  
  3281.         options[0] = '\0';
  3282.         if(*p == '/'){
  3283.             p2 = options;
  3284.             while(*p != '}')
  3285.               *p2++ = *p++;
  3286.  
  3287.             *p2 = '\0';
  3288.         }
  3289.  
  3290.         sprintf(tmp, "%s{%s%s}", (fn[0] == '*') ? "*" : "", host,
  3291.             options);
  3292.         ourstream++;
  3293.         if(!(find_stream = mail_open(NULL, tmp, OP_HALFOPEN))){
  3294.             if(we_cancel)
  3295.               cancel_busy_alarm(-1);
  3296.  
  3297.             return(-1);    /* mail_open should've displayed error */
  3298.         }
  3299.         }
  3300.     }
  3301.  
  3302.     find_string = mbox;
  3303.     }
  3304.     else
  3305.       find_string = fn;
  3306.  
  3307.     find_folder_list          = NULL;
  3308.     find_folder_count          = 0L;
  3309.     old_dot_state          = ps_global->show_dot_names;
  3310.     ps_global->show_dot_names = 1;         /* look everywhere */
  3311.     old_inbox_state          = find_folder_inbox;
  3312.     find_folder_inbox          = 1;        /* including "inbox" */
  3313.     if(find_stream && find_stream->mailbox && find_stream->mailbox[0] == '*')
  3314.       context_find_all_bboard(NULL, find_stream, find_string);
  3315.     else
  3316.       context_find_all(NULL, find_stream, find_string);
  3317.  
  3318.     ps_global->show_dot_names = old_dot_state;
  3319.     find_folder_inbox          = old_inbox_state;
  3320.     if(ourstream)
  3321.       mail_close(find_stream);
  3322.  
  3323.     if(we_cancel)
  3324.       cancel_busy_alarm(-1);
  3325.  
  3326.     return(find_folder_count > 0L);
  3327. }
  3328.  
  3329.  
  3330.  
  3331. /*----------------------------------------------------------------------
  3332.        Check to see if folder exists in given context
  3333.  
  3334.   Args: stream -- pointer to stream to use or create
  3335.     cntxt -- context to check for folder in
  3336.         folder -- name of folder to check for recent messages
  3337.  
  3338.  Result: returns 1 if there are recent messages (and *stream assigned)
  3339.                  0 if not
  3340.  
  3341.     ----*/
  3342. int
  3343. folder_has_recent(stream, cntxt, folder)
  3344.     MAILSTREAM **stream;
  3345.     CONTEXT_S    *cntxt;
  3346.     FOLDER_S    *folder;
  3347. {
  3348.     int         rv, we_cancel = 0;
  3349.     char       *fn;
  3350.     char        msg_buf[MAX_SCREEN_COLS+1];
  3351.  
  3352.     if(folder)
  3353.       fn = FLDR_NAME(folder);
  3354.     else
  3355.       return(0);
  3356.  
  3357.     strcat(strncat(strcpy(msg_buf, "Checking "), fn, 50),
  3358.        " for recent messages");
  3359.     we_cancel = busy_alarm(1, msg_buf, NULL, 1);
  3360.  
  3361.     /* current folder can't have recent */
  3362.     rv = ((ps_global->context_current != ps_global->context_list
  3363.        || strcmp(ps_global->cur_folder, fn))
  3364.       && stream
  3365.       && ((*stream) = context_open(cntxt->context, *stream,
  3366.                        folder->name, OP_READONLY))
  3367.       && (*stream)->recent > 0L);
  3368.  
  3369.     if(we_cancel)
  3370.       cancel_busy_alarm(0);
  3371.  
  3372.     return(rv);
  3373. }
  3374.  
  3375.  
  3376.  
  3377. /*----------------------------------------------------------------------
  3378.  Initialize global list of contexts for folder collections.
  3379.  
  3380.  Interprets collections defined in the pinerc and orders them for
  3381.  pine's use.  Parses user-provided context labels and sets appropriate 
  3382.  use use flags and the default prototype for that collection. 
  3383.  (See find_folders for how the actual folder list is found).
  3384.  
  3385.   ----*/
  3386. void
  3387. init_folders(ps)
  3388.     struct pine *ps;
  3389. {
  3390.     CONTEXT_S  *tc, *top = NULL, **clist, *prime = NULL;
  3391.     FOLDER_S   *f;
  3392.     int         num = 1, i;
  3393.  
  3394.     clist = ⊤
  3395.  
  3396.     /*
  3397.      * If no incoming folders are config'd, but the user asked for
  3398.      * them via feature, make sure at least "inbox" ends up there...
  3399.      */
  3400.     if(F_ON(F_ENABLE_INCOMING, ps) && !ps->VAR_INCOMING_FOLDERS){
  3401.     ps->VAR_INCOMING_FOLDERS    = (char **)fs_get(2 * sizeof(char *));
  3402.     ps->VAR_INCOMING_FOLDERS[0] = cpystr(ps->inbox_name);
  3403.     ps->VAR_INCOMING_FOLDERS[1] = NULL;
  3404.     }
  3405.  
  3406.     /*
  3407.      * Build context that's a list of folders the user's defined
  3408.      * as receiveing new messages.  At some point, this should
  3409.      * probably include adding a prefix with the new message count.
  3410.      */
  3411.     if(ps->VAR_INCOMING_FOLDERS && ps->VAR_INCOMING_FOLDERS[0]
  3412.        && (tc = new_context("Incoming-Folders []"))){ /* fake new context */
  3413.     tc->use    &= ~CNTXT_NOFIND;     /* mark all entries found */
  3414.     tc->use    |= CNTXT_INCMNG;    /* mark this as incoming collection */
  3415.     tc->num     = 0;
  3416.     if(tc->label[0])
  3417.       fs_give((void **)&tc->label[0]);
  3418.  
  3419.     tc->label[0] = cpystr("Incoming Message Folders");
  3420.  
  3421.     *clist = tc;
  3422.     clist  = &tc->next;
  3423.  
  3424.     for(i = 0; ps->VAR_INCOMING_FOLDERS[i] ; i++){
  3425.         char *folder_string, *nickname;
  3426.  
  3427.         /*
  3428.          * Parse folder line for nickname and folder name.
  3429.          * No nickname on line is OK.
  3430.          */
  3431.         get_pair(ps->VAR_INCOMING_FOLDERS[i], &nickname, &folder_string,0);
  3432.  
  3433.         /*
  3434.          * Allow for inbox to be specified in the incoming list, but
  3435.          * don't let it show up along side the one magically inserted
  3436.          * above!
  3437.          */
  3438.         if(!folder_string || !strucmp(ps->inbox_name, folder_string)){
  3439.         if(folder_string)
  3440.           fs_give((void **)&folder_string);
  3441.  
  3442.         if(nickname)
  3443.           fs_give((void **)&nickname);
  3444.  
  3445.         continue;
  3446.         }
  3447.  
  3448.         f = new_folder(folder_string);
  3449.         fs_give((void **)&folder_string);
  3450.         if(nickname){
  3451.         if(strucmp(ps->inbox_name, nickname)){
  3452.             f->nickname = nickname;
  3453.             f->name_len = strlen(f->nickname);
  3454.         }
  3455.         else
  3456.           fs_give((void **)&nickname);
  3457.         }
  3458.  
  3459.         folder_insert(f->nickname 
  3460.               && (strucmp(f->nickname, ps->inbox_name) == 0)
  3461.                 ? -1 : folder_total(tc->folders),
  3462.               f, tc->folders);
  3463.     }
  3464.     }
  3465.  
  3466.     /*
  3467.      * Build list of folder collections.  Because of the way init.c
  3468.      * works, we're guaranteed at least a default.  Also write any
  3469.      * "bogus format" messages...
  3470.      */
  3471.     for(i = 0; ps->VAR_FOLDER_SPEC && ps->VAR_FOLDER_SPEC[i] ; i++){
  3472.     if(tc = new_context(ps->VAR_FOLDER_SPEC[i])){
  3473.         if(!prime){
  3474.         prime       = tc;
  3475.         prime->use |= (CNTXT_PRIME | CNTXT_SAVEDFLT);
  3476.         }
  3477.         else
  3478.           tc->use  |= CNTXT_SECOND;
  3479.  
  3480.         tc->num     = num++;            /* place in the list */
  3481.         *clist      = tc;                /* add it to list   */
  3482.         clist       = &tc->next;            /* prepare for next */
  3483.     }
  3484.     }
  3485.  
  3486.  
  3487.     /*
  3488.      * Whoah cowboy!!!  Guess we couldn't find a valid folder
  3489.      * collection, so fall back on default...
  3490.      */
  3491.     if(!prime){
  3492.     char buf[MAXPATH];
  3493.  
  3494.     build_path(buf, ps->VAR_MAIL_DIRECTORY, "[]");
  3495.     tc          = new_context(buf);
  3496.     tc->use    |= (CNTXT_PRIME | CNTXT_SAVEDFLT);
  3497.     tc->num     = num++;                /* place in the list */
  3498.     *clist      = tc;                /* add it to list   */
  3499.     clist       = &tc->next;            /* prepare for next */
  3500.     }
  3501.  
  3502.     /*
  3503.      * At this point, insert the INBOX mapping as the leading
  3504.      * folder entry of the first collection...
  3505.      */
  3506.     init_inbox_mapping(ps->VAR_INBOX_PATH, top);
  3507.  
  3508.     /*
  3509.      * If no news collections spec'd, but an nntp-server defined, 
  3510.      * fake a default news collection.  Check only "user" and "global"
  3511.      * nntp_server values as the "current" value is influenced by
  3512.      *  env vars and other news config files (see init.c)...
  3513.      */
  3514.     if(!ps->VAR_NEWS_SPEC
  3515.        && ((ps->USR_NNTP_SERVER
  3516.         && ps->USR_NNTP_SERVER[0])
  3517.        || (ps->GLO_NNTP_SERVER
  3518.            && ps->GLO_NNTP_SERVER[0]))){
  3519.     char buf[MAXPATH];
  3520.  
  3521.     ps->VAR_NEWS_SPEC    = (char **)fs_get(2 * sizeof(char *));
  3522.     sprintf(buf, "*{%s/nntp}[]", ps->VAR_NNTP_SERVER[0]);
  3523.     ps->VAR_NEWS_SPEC[0] = cpystr(buf);
  3524.     ps->VAR_NEWS_SPEC[1] = NULL;
  3525.     }
  3526.  
  3527.     /*
  3528.      * If news groups, loop thru list adding to collection list
  3529.      */
  3530.     for(i = 0; ps->VAR_NEWS_SPEC && ps->VAR_NEWS_SPEC[i] ; i++){
  3531.     if(ps->VAR_NEWS_SPEC[i][0]
  3532.        && (tc = new_context(ps->VAR_NEWS_SPEC[i]))){
  3533.         tc->use    |= CNTXT_NEWS;
  3534.         tc->num     = num++;
  3535.         *clist      = tc;            /* add it to list   */
  3536.         clist       = &tc->next;        /* prepare for next */
  3537.     }
  3538.     }
  3539.  
  3540.     ps->context_list    = top;    /* init pointers */
  3541.     ps->context_current = (top->use & CNTXT_INCMNG) ? top->next : top;
  3542. #ifdef    DEBUG
  3543.     if(debug > 7 && do_debug(debugfile))
  3544.       dump_contexts(debugfile);
  3545. #endif
  3546. }
  3547.  
  3548.  
  3549.  
  3550. #ifdef    DEBUG
  3551. dump_contexts(fp)
  3552.     FILE *fp;
  3553. {
  3554.     FOLDER_S *f;
  3555.     int i = 0;
  3556.     CONTEXT_S *c = ps_global->context_list;
  3557.  
  3558.     while(fp && c != NULL){
  3559.     fprintf(fp, "\n***** context %s\n", c->context);
  3560.     for(i=0;c->label[i] != NULL;i++)
  3561.       fprintf(fp,"LABEL: %s\n", c->label[i]);
  3562.     
  3563.     for(i = 0; i < folder_total(c->folders); i++)
  3564.       fprintf(fp, "  %d) %s\n", i + 1, folder_entry(i,c->folders)->name);
  3565.     
  3566.     c = c->next;
  3567.     }
  3568. }
  3569. #endif
  3570.  
  3571.  
  3572. /*
  3573.  * Return malloc'd string containing only the context specifier given
  3574.  * a string containing the raw pinerc entry
  3575.  */
  3576. char *
  3577. context_string(s)
  3578.     char *s;
  3579. {
  3580.     CONTEXT_S *t = new_context(s);
  3581.     char      *rv = NULL;
  3582.     int           i;
  3583.  
  3584.     if(t){
  3585.     /*
  3586.      * Take what we want from context, then free the rest...
  3587.      */
  3588.     rv = t->context;
  3589.     t->context = NULL;            /* don't free it! */
  3590.     free_context(&t);
  3591.     }
  3592.  
  3593.     return(rv ? rv : cpystr(""));
  3594. }
  3595.  
  3596.  
  3597. /*
  3598.  *  Release resources associated with global context list
  3599.  */
  3600. void
  3601. free_folders()
  3602. {
  3603.     CONTEXT_S  *blast;
  3604.  
  3605.     while(blast = ps_global->context_list){
  3606.     ps_global->context_list = ps_global->context_list->next;
  3607.     free_context(&blast);            /* blast the context */
  3608.     }
  3609.  
  3610.     ps_global->context_current = NULL;
  3611. }
  3612.  
  3613.  
  3614. /*
  3615.  *  Release resources associated with the given context structure
  3616.  */
  3617. void
  3618. free_context(cntxt)
  3619.     CONTEXT_S **cntxt;
  3620. {
  3621.     char **labels;
  3622.  
  3623.     if((*cntxt)->context)
  3624.       fs_give((void **)&(*cntxt)->context);
  3625.     
  3626.     for(labels = (*cntxt)->label; *labels; labels++)
  3627.       fs_give((void **)labels);
  3628.     
  3629.     if((*cntxt)->nickname)
  3630.       fs_give((void **)&(*cntxt)->nickname);
  3631.  
  3632.     if((*cntxt)->folders)
  3633.       free_folder_list((void **)&(*cntxt)->folders);
  3634.  
  3635.     fs_give((void **)cntxt);
  3636. }
  3637.  
  3638.  
  3639. /*
  3640.  *
  3641.  */
  3642. void
  3643. init_inbox_mapping(path, cntxt)
  3644.      char      *path;
  3645.      CONTEXT_S *cntxt;
  3646. {
  3647.     FOLDER_S *f;
  3648.  
  3649.     /*
  3650.      * If mapping already exists, blast it and replace it below...
  3651.      */
  3652.     if((f = folder_entry(0, cntxt->folders))
  3653.        && f->nickname && !strcmp(f->nickname, ps_global->inbox_name))
  3654.       folder_delete(0, cntxt->folders);
  3655.  
  3656.     if(path){
  3657.     f = new_folder(path);
  3658.     f->nickname = cpystr(ps_global->inbox_name);
  3659.     f->name_len = strlen(f->nickname);
  3660.     }
  3661.     else
  3662.       f = new_folder(ps_global->inbox_name);
  3663.  
  3664.     folder_insert(0, f, cntxt->folders);
  3665. }
  3666.  
  3667.  
  3668. /*
  3669.  * returns with the folder's type's set
  3670.  *
  3671.  * WARNING: c-client naming knowledge is hardcoded here!
  3672.  *          This is based on our understanding of c-client naming 
  3673.  *        conventions:
  3674.  *
  3675.  *        leading '*' means local or remote bboard (news)
  3676.  *        leading '{' means remote access
  3677.  *        {XXX/nntp}  means remote nntp access
  3678.  *        {XXX/imap}  means remote imap access
  3679.  *        {XXX/anonymous}  means anonymous access
  3680. BUG? look into using mail.c:mail_valid_net()
  3681.  */
  3682. void
  3683. set_ftype(context, flags)
  3684.     char           *context;
  3685.     unsigned short *flags;
  3686. {
  3687.     char *p, tmp[MAXPATH];
  3688.     long  i;
  3689.  
  3690.     *flags = 0;                    /* clear flags */
  3691.  
  3692.     if(!context || *context == '\0')
  3693.       return;
  3694.  
  3695.     if(*context == '*'){
  3696.     *flags |= FTYPE_BBOARD;
  3697.     context++;
  3698.     }
  3699.  
  3700.     if(*context == '{' && context[1] && context[1] != '}' 
  3701.        && (p = strindex(context, '}'))){
  3702.     *flags |= FTYPE_REMOTE;
  3703.     i = p - context;
  3704.     strncpy(tmp, context, (int)i);
  3705.     tmp[i] = '\0';
  3706.     if(*p == '*')
  3707.       *flags |= FTYPE_BBOARD;
  3708.  
  3709.     if((p = strindex(tmp, '/')) && strucmp(p+1, "nntp") == 0)
  3710.       *flags |= FTYPE_OLDTECH;
  3711.  
  3712.     if(p && strucmp(p+1, "anonymous") == 0)
  3713.       *flags |= FTYPE_ANON;
  3714.     }
  3715.     else
  3716.       *flags |= FTYPE_LOCAL;        /* if it's not remote... */
  3717. }
  3718.  
  3719.  
  3720.  
  3721. /*
  3722.  * new_context - creates and fills in a new context structure, leaving
  3723.  *               blank the actual folder list (to be filled in later as
  3724.  *               needed).  Also, parses the context string provided 
  3725.  *               picking out any user defined label.  Context lines are
  3726.  *               of the form:
  3727.  *
  3728.  *               [ ["] <string> ["] <white-space>] <context>
  3729.  *
  3730.  */
  3731. CONTEXT_S *
  3732. new_context(cntxt_string)
  3733.     char *cntxt_string;
  3734. {
  3735.     CONTEXT_S  *c;
  3736.     char        host[MAXPATH], rcontext[MAXPATH], *nickname = NULL,
  3737.         *c_string = NULL, *p;
  3738.  
  3739.     /*
  3740.      * do any context string parsing (like splitting user-supplied 
  3741.      * label from actual context)...
  3742.      */
  3743.     get_pair(cntxt_string, &nickname, &c_string, 0);
  3744.  
  3745.     if(p = context_digest(c_string, NULL, host, rcontext, NULL)){
  3746.     q_status_message2(SM_ORDER | SM_DING, 3, 4,
  3747.               "Bad context, %s : %s", p, c_string);
  3748.     if(nickname)
  3749.       fs_give((void **)&nickname);
  3750.  
  3751.     return(NULL);
  3752.     }
  3753.  
  3754.     c = (CONTEXT_S *) fs_get(sizeof(CONTEXT_S)); /* get new context   */
  3755.     memset((void *) c, 0, sizeof(CONTEXT_S));    /* and initialize it */
  3756.     set_ftype(c_string, &(c->type));
  3757.  
  3758.     if(c->label[0] == NULL){
  3759.     if(!nickname){            /* make one up! */
  3760.         sprintf(tmp_20k_buf, "%s%s%s",
  3761.             (c->type & FTYPE_BBOARD) ? "News" : rcontext, 
  3762.             (c->type & FTYPE_REMOTE) ? " on " : "",
  3763.             (c->type & FTYPE_REMOTE) ? host : "");
  3764.         c->label[0]   = cpystr(tmp_20k_buf);
  3765.     }
  3766.     else
  3767.       c->label[0] = nickname;
  3768.     }
  3769.  
  3770.     c->context = c_string;
  3771.     c->use     = CNTXT_NOFIND;        /* do find later! */
  3772.     c->folders = new_folder_list();
  3773.  
  3774.  
  3775.     dprint(2, (debugfile, "Context %s type:%s%s%s%s%s%s\n", c->context,
  3776.            (c->type&FTYPE_LOCAL)   ? " LOCAL"   : "",
  3777.            (c->type&FTYPE_REMOTE)  ? " REMOTE"  : "",
  3778.            (c->type&FTYPE_SHARED)  ? " SHARED"  : "",
  3779.            (c->type&FTYPE_BBOARD)  ? " BBOARD"  : "",
  3780.            (c->type&FTYPE_OLDTECH) ? " OLDTECH"  : "",
  3781.            (c->type&FTYPE_ANON)    ? " ANON" : ""));
  3782.  
  3783.     return(c);
  3784. }
  3785.  
  3786.  
  3787.  
  3788. /*
  3789.  * find_folders_in_context - find the folders associated with the given context
  3790.  *                     and search pattern.  If the search pattern is not
  3791.  *                     the wild card ("*"), then some subset of all folders
  3792.  *                     is specified. So, don't mark context as completely
  3793.  *                     searched, but do set the bit to avoid recursive
  3794.  *                     calls...
  3795.  *
  3796.  *        If no search_string, we're being called to search the entire
  3797.  *        view within the given context.
  3798.  *
  3799.  *    NOTE: The first arg, "stream", is here for efficiency.
  3800.  *          If it's set, then the caller want's the stream we opened
  3801.  *          for the context_find(), so don't close it when we leave.
  3802.  *          If we don't use it and it's set, mail_close and NULL it.
  3803.  *          If we use another stream and it's set, close the old one
  3804.  *          and reset it to the new one.  It's up to the caller to
  3805.  *          make sure it get's closed properly.
  3806.  *
  3807.  */
  3808. void
  3809. find_folders_in_context(stream, context, search_string)
  3810.     MAILSTREAM **stream;
  3811.     CONTEXT_S    *context;
  3812.     char    *search_string;
  3813. {
  3814.     char  host[MAXPATH], rcontext[MAXPATH], view[MAXPATH],
  3815.          *search_context, *rv;
  3816.     int   local_open = 0, we_cancel = 0;
  3817.  
  3818.     if((context->use&CNTXT_NOFIND) == 0 || (context->use&CNTXT_PARTFIND))
  3819.       return;                /* find already done! */
  3820.  
  3821.     if(rv = context_digest(context->context, NULL, NULL, rcontext, view)){
  3822.     q_status_message2(SM_ORDER | SM_DING, 3, 3, "Bad context, %s : %s",
  3823.               rv, context->context);
  3824.     return;
  3825.     }
  3826.  
  3827.     we_cancel = busy_alarm(1, NULL, NULL, 0);
  3828.  
  3829.     if(!search_string || (search_string[0]=='*' && search_string[1]=='\0')){
  3830.     context->use &= ~CNTXT_NOFIND;    /* let'em know we tried  */
  3831.     search_string = view;        /* return full view of context */
  3832.     }
  3833.     else
  3834.       context->use |= CNTXT_PARTFIND;    /* or are in a partial find */
  3835.  
  3836.     /* let context_mailbox know context! */
  3837.     current_context   = context->context;
  3838.     find_folder_list  = context->folders;
  3839.  
  3840.     dprint(7, (debugfile, "find_folders_in_context: %s\n",
  3841.            context->context));
  3842.  
  3843.     /*
  3844.      * Here's where we have to be careful.  C-client requires different
  3845.      * search strings and inconsistently returns results based on 
  3846.      * what sort of technology you're interested in... (would be nice to 
  3847.      * see this cleaned up)
  3848.      *
  3849.      *     Type      Search           Search returns
  3850.      *     ----      ------           --------------
  3851.      * local mail    mail/*           /usr/staff/mikes/mail/foo
  3852.      * imap mail     {remote}mail/*   nothing (unless {} in mboxlist names)
  3853.      * imap mail     mail/*           {remote}mail/foo
  3854.      * local news    *                foo.bar (if news spool local!)
  3855.      * imap news     *                ????
  3856.      * nntp news     *                foo.bar
  3857.      * nntp news     [host]*          [host]foo.bar
  3858.      *
  3859.      */
  3860.  
  3861.     find_folder_stream = NULL;
  3862.     if((context->type)&FTYPE_REMOTE){
  3863.     char *stream_name;
  3864.     search_context = rcontext;
  3865.  
  3866.     sprintf(tmp_20k_buf, "%.*s}",
  3867.         strindex(context->context, '}') - context->context, 
  3868.         context->context);
  3869.  
  3870.     stream_name = cpystr(tmp_20k_buf);
  3871.  
  3872.     /*
  3873.      * Try using a stream we've already got open...
  3874.      */
  3875.     if(stream && *stream
  3876.        && !(find_folder_stream = same_stream(stream_name, *stream))){
  3877.         mail_close(*stream);
  3878.         *stream = NULL;
  3879.     }
  3880.  
  3881.     if(!find_folder_stream)
  3882.       find_folder_stream = same_stream(stream_name,
  3883.                        ps_global->mail_stream);
  3884.  
  3885.     if(!find_folder_stream 
  3886.        && ps_global->mail_stream != ps_global->inbox_stream)
  3887.       find_folder_stream = same_stream(stream_name,
  3888.                        ps_global->inbox_stream);
  3889.  
  3890.     /* gotta open a new one */
  3891.     if(!find_folder_stream){
  3892.         if((context->type&FTYPE_OLDTECH) && !(context->use&CNTXT_FINDALL)){
  3893.         find_folder_stream = mail_open(NULL, stream_name,OP_PROTOTYPE);
  3894.         }
  3895.         else{
  3896.         local_open++;
  3897.         find_folder_stream = mail_open(NULL, stream_name, OP_HALFOPEN);
  3898.         if(stream)
  3899.           *stream = find_folder_stream;
  3900.         }
  3901.     }
  3902.  
  3903.     dprint(find_folder_stream ? 7 : 1,
  3904.            (debugfile, "find_folders: mail_open(%s) %s.\n",
  3905.         stream_name, find_folder_stream ? "OK" : "FAILED"));
  3906.     fs_give((void **)&stream_name);
  3907.     if(!find_folder_stream){
  3908.         context->use &= ~CNTXT_PARTFIND;    /* unset partial find bit */
  3909.         if(we_cancel)
  3910.           cancel_busy_alarm(-1);
  3911.  
  3912.         return;
  3913.     }
  3914.     }
  3915.     else{
  3916.     search_context = rcontext;
  3917.     if(stream && *stream){
  3918.         mail_close(*stream);
  3919.         *stream = NULL;
  3920.     }
  3921.     }
  3922.  
  3923.     ps_global->show_dot_names = F_ON(F_ENABLE_DOT_FOLDERS, ps_global);
  3924.  
  3925.     if(context->type & FTYPE_BBOARD){            /* get the list */
  3926. #ifdef NEWBB
  3927.         if(context->use & CNTXT_NEWBB) 
  3928.           context_find_new_bboard(search_context, find_folder_stream,
  3929.                              search_string,
  3930.                              ctime2nntp(ps_global->VAR_NNTP_NEW_GROUP_TIME));
  3931.         else
  3932. #endif
  3933.         if(context->use & CNTXT_FINDALL)
  3934.           context_find_all_bboard(search_context, find_folder_stream,
  3935.                                search_string);
  3936.         else 
  3937.        context_find_bboards(search_context, find_folder_stream,
  3938.                                  search_string);
  3939.  
  3940.         if((F_OFF(F_READ_IN_NEWSRC_ORDER,ps_global) ||
  3941.             context->use & CNTXT_FINDALL)
  3942. #ifdef NEWBB
  3943.           && !(context->use & CNTXT_NEWBB)
  3944. #endif
  3945.             )
  3946.            sort_folder_list(context->folders, compare_folders);
  3947.     }
  3948.     else{
  3949.     /*
  3950.      * For now use find_all as there's no distinguishing in pine
  3951.      * between subscribed and not with regard to mailboxes.  At some
  3952.      * point we may want to use the subscription mechanism to help 
  3953.      * performance, but implicit subscription needs to be added and 
  3954.      * folders created outside pine won't be automatically visible
  3955.      *
  3956.      * THOUGHT: the first find as pine starts does a find_all and then  
  3957.      * a find then reconciles the two automatically subscribing
  3958.      * any new folders.  We can then easily rebuild folder list on 
  3959.      * every folder menu access much cheaper (locally anyway) 
  3960.      * (probably only necessary to rebuild the section that has
  3961.      * to do with the current context)
  3962.      */
  3963.     context_find_all(search_context, find_folder_stream, search_string);
  3964.     }
  3965.  
  3966.     if(local_open && !stream)
  3967.       mail_close(find_folder_stream);
  3968.  
  3969.     context->use &= ~CNTXT_PARTFIND;    /* unset partial find bit */
  3970.     if(we_cancel)
  3971.       cancel_busy_alarm(-1);
  3972. }
  3973.  
  3974.  
  3975.  
  3976. /*
  3977.  * free_folders_in_context - loop thru the context's lists of folders
  3978.  *                     clearing all entries without nicknames
  3979.  *                     (as those were user provided) AND reset the 
  3980.  *                     context's find flag.
  3981.  *
  3982.  * NOTE: if fetched() information (e.g., like message counts come back
  3983.  *       in bboard collections), we may want to have a check before
  3984.  *       executing the loop and setting the FIND flag.
  3985.  */
  3986. void
  3987. free_folders_in_context(cntxt)
  3988.     CONTEXT_S *cntxt;
  3989. {
  3990.     int n, i;
  3991.  
  3992.     /*
  3993.      * In this case, don't blast the list as it was given to us by the
  3994.      * user and not the result of a mail_find* call...
  3995.      */
  3996.     if(cntxt->use & CNTXT_INCMNG)
  3997.       return;
  3998.  
  3999.     for(n = folder_total(cntxt->folders), i = 0; n > 0; n--)
  4000.       if(folder_entry(i, cntxt->folders)->nickname)
  4001.     i++;                /* entry wasn't a FIND result */
  4002.       else
  4003.     folder_delete(i, cntxt->folders);
  4004.  
  4005.     cntxt->use |= CNTXT_NOFIND;        /* do find next time...  */
  4006.     cntxt->use &= ~CNTXT_PSEUDO;    /* or add the fake entry */
  4007. }
  4008.  
  4009.  
  4010. /*
  4011.  * default_save_context - return the default context for saved messages
  4012.  */
  4013. CONTEXT_S *
  4014. default_save_context(cntxt)
  4015.     CONTEXT_S *cntxt;
  4016. {
  4017.     while(cntxt)
  4018.       if((cntxt->use) & CNTXT_SAVEDFLT)
  4019.     return(cntxt);
  4020.       else
  4021.     cntxt = cntxt->next;
  4022.  
  4023.     return(NULL);
  4024. }
  4025.  
  4026.  
  4027.  
  4028. /*----------------------------------------------------------------------
  4029.     Update the folder display structure for the number of unread
  4030.  messages for a news group
  4031.  
  4032. Args: stream   -- open mail stream to count unread messages on
  4033.       f_struct -- pointer to the structure to update
  4034.  
  4035. This is called when going into the folders screen or when closing a
  4036. news group to update the string that is displayed showing the number
  4037. of unread messages. The stream and f_struct passed in better be for
  4038. the same folder or things will get a little confused. 
  4039.  
  4040. This can also be a little slow because count_flagged() can be slow
  4041. due to current performance of the news driver.
  4042.  ----*/
  4043. void
  4044. update_news_prefix(stream, f_struct)
  4045.      MAILSTREAM *stream;
  4046.      struct folder *f_struct;
  4047. {
  4048.    int n;
  4049.  
  4050.    n = count_flagged(stream, "UNSEEN");
  4051.    sprintf(f_struct->prefix, "%4.4s ",  n ? int2string(n)  : "");
  4052. }
  4053.  
  4054.  
  4055.  
  4056. /*
  4057.  * folder_complete - foldername completion routine
  4058.  *              returns:
  4059.  *
  4060.  *   Result: returns 0 if the folder doesn't have a unique completetion
  4061.  *                   1 if so, and replaces name with completion
  4062.  */
  4063. folder_complete(context, name)
  4064.     CONTEXT_S *context;
  4065.     char      *name;
  4066. {
  4067.     int  i, match = -1, did_find;
  4068.     char tmp[MAXFOLDER], tmpname[MAXFOLDER], *a, *b; 
  4069.     FOLDER_S *f;
  4070. #if defined(DOS) || defined(OS2)
  4071. #define    STRUCMP    struncmp         /* ignore case under DOS */
  4072. #else
  4073. #define    STRUCMP    strncmp
  4074. #endif
  4075.     
  4076.     if(*name == '\0' || !context_isambig(name))
  4077.       return(0);
  4078.  
  4079.     if(did_find = !ALL_FOUND(context)){
  4080.     sprintf(tmpname, "%s*", name);
  4081.     find_folders_in_context(NULL, context, tmpname);
  4082.     }
  4083.     else if(context->use & CNTXT_PSEUDO)
  4084.       return(0);            /* no folders to complete */
  4085.  
  4086.     *tmp = '\0';            /* find uniq substring */
  4087.     for(i = 0; i < folder_total(context->folders); i++){
  4088.     f = folder_entry(i, context->folders);
  4089.     if(STRUCMP(name, FLDR_NAME(f), strlen(name)) == 0){
  4090.         if(match != -1){        /* oh well, do best we can... */
  4091.         a = FLDR_NAME(f);
  4092.         if(match >= 0){
  4093.             f = folder_entry(match, context->folders);
  4094.             strcpy(tmp, FLDR_NAME(f));
  4095.         }
  4096.  
  4097.         match = -2;
  4098.         b     = tmp;        /* remember largest common text */
  4099.         while(*a && *b && *a == *b)
  4100.           *b++ = *a++;
  4101.  
  4102.         *b = '\0';
  4103.         }
  4104.         else        
  4105.           match = i;        /* bingo?? */
  4106.     }
  4107.     }
  4108.  
  4109.     if(match >= 0){            /* found! */
  4110.     f = folder_entry(match, context->folders);
  4111.     strcpy(name, FLDR_NAME(f));
  4112.     }
  4113.     else if(match == -2)        /* closest we could find */
  4114.       strcpy(name, tmp);
  4115.  
  4116.     if(did_find){
  4117.     if(context->use & CNTXT_PSEUDO){
  4118.         while(folder_total(context->folders) > 1)
  4119.           folder_delete(strcmp(folder_entry(0, context->folders)->name,
  4120.                    CLICKHERE) ? 0 : 1,
  4121.                 context->folders);
  4122.     }
  4123.     else
  4124.       free_folders_in_context(context);
  4125.     }
  4126.  
  4127.     return((match >= 0) ? 1 : 0);
  4128. }
  4129.  
  4130.  
  4131. /*
  4132.  *           FOLDER MANAGEMENT ROUTINES
  4133.  */
  4134.  
  4135.  
  4136. /*
  4137.  * Folder List Structure - provides for two ways to access and manage
  4138.  *                         folder list data.  One as an array of pointers
  4139.  *                         to folder structs or
  4140.  */
  4141. typedef struct folder_list {
  4142.     unsigned   used;
  4143.     unsigned   allocated;
  4144. #ifdef    DOSXXX
  4145.     FILE      *folders;        /* tmpfile of binary */
  4146. #else
  4147.     FOLDER_S **folders;        /* array of pointers to folder structs */
  4148. #endif
  4149. } FLIST;
  4150.  
  4151. #define FCHUNK  64
  4152.  
  4153.  
  4154. /*
  4155.  * new_folder_list - return a piece of memory suitable for attaching 
  4156.  *                   a list of folders...
  4157.  *
  4158.  */
  4159. void *
  4160. new_folder_list()
  4161. {
  4162.     FLIST *flist = (FLIST *) fs_get(sizeof(FLIST));
  4163.     flist->folders = (FOLDER_S **) fs_get(FCHUNK * sizeof(FOLDER_S *));
  4164.     memset((void *)flist->folders, 0, (FCHUNK * sizeof(FOLDER_S *)));
  4165.     flist->allocated = FCHUNK;
  4166.     flist->used      = 0;
  4167.     return((void *)flist);
  4168. }
  4169.  
  4170.  
  4171.  
  4172. /*
  4173.  * new_folder - return a brand new folder entry, with the given name
  4174.  *              filled in.
  4175.  *
  4176.  * NOTE: THIS IS THE ONLY WAY TO PUT A NAME INTO A FOLDER ENTRY!!!
  4177.  * STRCPY WILL NOT WORK!!!
  4178.  */
  4179. FOLDER_S *
  4180. new_folder(name)
  4181.     char *name;
  4182. {
  4183. #ifdef    DOSXXX
  4184. #else
  4185.     FOLDER_S *tmp;
  4186.     size_t    l = strlen(name);
  4187.  
  4188.     tmp = (FOLDER_S *)fs_get(sizeof(FOLDER_S) + (l * sizeof(char)));
  4189.     memset((void *)tmp, 0, sizeof(FOLDER_S));
  4190.     strcpy(tmp->name, name);
  4191.     tmp->name_len = (unsigned char) l;
  4192.     return(tmp);
  4193. #endif
  4194. }
  4195.  
  4196.  
  4197.  
  4198. /*
  4199.  * folder_entry - folder struct access routine.  Permit reference to
  4200.  *                folder structs via index number. Serves two purposes:
  4201.  *            1) easy way for callers to access folder data 
  4202.  *               conveniently
  4203.  *            2) allows for a custom manager to limit memory use
  4204.  *               under certain rather limited "operating systems"
  4205.  *               who shall renameless, but whose initials are DOS
  4206.  *
  4207.  *
  4208.  */
  4209. FOLDER_S *
  4210. folder_entry(i, flist)
  4211.     int   i;
  4212.     void *flist;
  4213. {
  4214. #ifdef    DOSXXX
  4215.     /*
  4216.      * Manage entries within a limited amount of core (what a drag).
  4217.      */
  4218.  
  4219.     fseek((FLIST *)flist->folders, i * sizeof(FOLDER_S) + MAXPATH, 0);
  4220.     fread(&folder, sizeof(FOLDER_S) + MAXPATH, (FLIST *)flist->folders);
  4221. #else
  4222.     return((i >= ((FLIST *)flist)->used) ? NULL:((FLIST *)flist)->folders[i]);
  4223. #endif
  4224. }
  4225.  
  4226.  
  4227.  
  4228. /*
  4229.  * free_folder_list - release all resources associated with the given 
  4230.  *                    folder list
  4231.  */
  4232. void
  4233. free_folder_list(flist)
  4234.     void **flist;
  4235. {
  4236. #ifdef    DOSXXX
  4237.     fclose((*((FLIST **)flist))->folders);     /* close folder tmpfile */
  4238. #else
  4239.     register int i = (*((FLIST **)flist))->used;
  4240.  
  4241.     while(i--){
  4242.     if((*((FLIST **)flist))->folders[i]->nickname)
  4243.       fs_give((void **)&(*((FLIST **)flist))->folders[i]->nickname);
  4244.  
  4245.     fs_give((void **)&((*((FLIST **)flist))->folders[i]));
  4246.     }
  4247.  
  4248.     fs_give((void **)&((*((FLIST **)flist))->folders));
  4249. #endif
  4250.     fs_give(flist);
  4251. }
  4252.  
  4253.  
  4254.  
  4255. /*
  4256.  * return the number of folders associated with the given folder list
  4257.  */
  4258. int
  4259. folder_total(flist)
  4260.     void *flist;
  4261. {
  4262.     return((int)((FLIST *)flist)->used);
  4263. }
  4264.  
  4265.  
  4266. /*
  4267.  * return the index number of the given name in the given folder list
  4268.  */
  4269. int
  4270. folder_index(name, flist)
  4271.     char *name;
  4272.     void *flist;
  4273. {
  4274.     register  int i = 0;
  4275.     FOLDER_S *f;
  4276.     char     *fname;
  4277.  
  4278.     while(f = folder_entry(i, flist)){
  4279.     fname = FLDR_NAME(f);
  4280. #if defined(DOS) || defined(OS2)
  4281.     if(toupper(*name) == toupper(*fname) && strucmp(name, fname) == 0)
  4282. #else
  4283.     if(*name == *fname && strcmp(name, fname) == 0)
  4284. #endif
  4285.       return(i);
  4286.     else
  4287.       i++;
  4288.     }
  4289.  
  4290.     return(-1);
  4291. }
  4292.  
  4293.  
  4294.  
  4295. /*
  4296.  * next_folder - given a current folder in a context, return the next in
  4297.  *               the list, or NULL if no more or there's a problem.
  4298.  */
  4299. char *
  4300. next_folder(stream, next, current, cntxt, find_recent)
  4301.     MAILSTREAM **stream;
  4302.     char    *current, *next;
  4303.     CONTEXT_S    *cntxt;
  4304.     int         find_recent;
  4305. {
  4306.     int       index, are_recent = 0;
  4307.     FOLDER_S *f = NULL;
  4308.  
  4309.     /* note: find_folders may assign "stream" */
  4310.     find_folders_in_context(stream, cntxt, NULL);
  4311.     for(index = folder_index(current, cntxt->folders) + 1;
  4312.     index > 0
  4313.     && index < folder_total(cntxt->folders)
  4314.     && (f = folder_entry(index, cntxt->folders));
  4315.     index++)
  4316.       if(find_recent){
  4317.       /* if we can't use this stream, close it up */
  4318.       if(stream && *stream
  4319.          && !context_same_stream(cntxt->context, f->name, *stream)){
  4320.           mail_close(*stream);
  4321.           *stream = NULL;
  4322.       }
  4323.  
  4324.       if(are_recent = folder_has_recent(stream, cntxt,f))
  4325.         break;
  4326.       }
  4327.  
  4328.     if(f && (!find_recent || are_recent))
  4329.       strcpy(next, FLDR_NAME(f));
  4330.     else
  4331.       *next = '\0';
  4332.  
  4333.     /* BUG: how can this be made smarter so we cache the list? */
  4334.     free_folders_in_context(cntxt);
  4335.     return((*next) ? next : NULL);
  4336. }
  4337.  
  4338.  
  4339.  
  4340. /*
  4341.  * folder_is_nick - check to see if the given name is a nickname
  4342.  *                  for some folder in the given context...
  4343.  *
  4344.  *  NOTE: no need to check if find_folder_names has been done as 
  4345.  *        nicknames can only be set by configuration...
  4346.  */
  4347. char *
  4348. folder_is_nick(nickname, flist)
  4349.     char *nickname;
  4350.     void *flist;
  4351. {
  4352.     register  int  i = 0;
  4353.     FOLDER_S *f;
  4354.  
  4355.     while(f = folder_entry(i, flist)){
  4356.     if(f->nickname && STRCMP(nickname, f->nickname) == 0)
  4357.       return(f->name);
  4358.     else
  4359.       i++;
  4360.     }
  4361.  
  4362.     return(NULL);
  4363. }
  4364.  
  4365.  
  4366.  
  4367. /*----------------------------------------------------------------------
  4368.   Insert the given folder name into the sorted folder list
  4369.   associated with the given context.  Only allow ambiguous folder
  4370.   names IF associated with a nickname.
  4371.  
  4372.    Args: index  -- Index to insert at, OR insert in sorted order if -1
  4373.          folder -- folder structure to insert into list
  4374.      flist  -- folder list to insert folder into
  4375.  
  4376.   **** WARNING ****
  4377.   DON'T count on the folder pointer being valid after this returns
  4378.   *** ALL FOLDER ELEMENT READS SHOULD BE THRU folder_entry() ***
  4379.  
  4380.   ----*/
  4381. int
  4382. folder_insert(index, folder, flist)
  4383.     int       index;
  4384.     FOLDER_S *folder;
  4385.     void     *flist;
  4386. {
  4387.     int i;
  4388.  
  4389.     if(index < 0){            /* add to sorted list */
  4390.     char     *sortname, *fsortname;
  4391.     FOLDER_S *f;
  4392.  
  4393.     sortname = FLDR_NAME(folder);
  4394.     index    = 0;
  4395.     while(f = folder_entry(index, flist)){
  4396.         fsortname = FLDR_NAME(f);
  4397.         if((i = compare_names(&sortname, &fsortname)) < 0)
  4398.           break;
  4399.         else if(i == 0)        /* same folder? just return index */
  4400.           return(index);
  4401.         else
  4402.           index++;
  4403.     }
  4404.     }
  4405.  
  4406.     folder_insert_index(folder, index, flist);
  4407.     return(index);
  4408. }
  4409.  
  4410.  
  4411. /* 
  4412.  * folder_insert_index - Insert the given folder struct into the global list
  4413.  *                 at the given index.
  4414.  */
  4415. void
  4416. folder_insert_index(folder, index, flist)
  4417.     FOLDER_S *folder;
  4418.     int       index;
  4419.     void     *flist;
  4420. {
  4421. #ifdef    DOSXXX
  4422.     FOLDER *tmp;
  4423.  
  4424.     tmp = (FOLDER_S *)fs_get(sizeof(FOLDER_S) + (MAXFOLDER * sizeof(char)));
  4425.  
  4426.  
  4427. #else
  4428.     register FOLDER_S **flp, **iflp;
  4429.  
  4430.     /* if index is beyond size, place at end of list */
  4431.     index = min(index, ((FLIST *)flist)->used);
  4432.  
  4433.     /* grow array ? */
  4434.     if(((FLIST *)flist)->used + 1 > ((FLIST *)flist)->allocated){
  4435.     ((FLIST *)flist)->allocated += FCHUNK;
  4436.     fs_resize((void **)&(((FLIST *)flist)->folders),
  4437.           (((FLIST *)flist)->allocated) * sizeof(FOLDER_S *));
  4438.     }
  4439.  
  4440.     /* shift array left */
  4441.     iflp = &(((FOLDER_S **)((FLIST *)flist)->folders)[index]);
  4442.     for(flp = &(((FOLDER_S **)((FLIST *)flist)->folders)[((FLIST *)flist)->used]); 
  4443.     flp > iflp; flp--)
  4444.       flp[0] = flp[-1];
  4445.  
  4446.     ((FLIST *)flist)->folders[index] = folder;
  4447.     ((FLIST *)flist)->used          += 1;
  4448. #endif
  4449. }
  4450.  
  4451.  
  4452. /*----------------------------------------------------------------------
  4453.     Removes a folder at the given index in the given context's
  4454.     list.
  4455.  
  4456. Args: index -- Index in folder list of folder to be removed
  4457.       flist -- folder list
  4458.  ----*/
  4459. void
  4460. folder_delete(index, flist)
  4461.     int   index;
  4462.     void *flist;
  4463. {
  4464.     register int  i;
  4465.     FOLDER_S     *f;
  4466.  
  4467.     if(((FLIST *)flist)->used 
  4468.        && (index < 0 || index >= ((FLIST *)flist)->used))
  4469.       return;                /* bogus call! */
  4470.  
  4471.     if((f = folder_entry(index, flist))->nickname)
  4472.       fs_give((void **)&(f->nickname));
  4473.       
  4474. #ifdef    DOSXXX
  4475.     /* shift all entries after index up one.... */
  4476. #else
  4477.     fs_give((void **)&(((FLIST *)flist)->folders[index]));
  4478.     for(i = index; i < ((FLIST *)flist)->used - 1; i++)
  4479.       ((FLIST *)flist)->folders[i] = ((FLIST *)flist)->folders[i+1];
  4480.  
  4481.  
  4482.     ((FLIST *)flist)->used -= 1;
  4483. #endif
  4484. }
  4485.  
  4486.  
  4487.  
  4488. /*----------------------------------------------------------------------
  4489.     Find an entry in the folder list by matching names
  4490.   ----*/
  4491. search_folder_list(list, name)
  4492.      void *list;
  4493.      char *name;
  4494. {
  4495.     int i;
  4496.     char *n;
  4497.  
  4498.     for(i = 0; i < folder_total(list); i++) {
  4499.         n = folder_entry(i, list)->name;
  4500.         if(strucmp(name, n) == 0)
  4501.           return(1); /* Found it */
  4502.     }
  4503.     return(0);
  4504. }
  4505.  
  4506.  
  4507.  
  4508. /*----------------------------------------------------------------------
  4509.    Sort the given folder list
  4510.   ----*/
  4511. sort_folder_list(list, sort)
  4512.      void  *list;
  4513.      QSFunc sort;
  4514. {
  4515.     qsort((QSType *)(((FLIST *)list)->folders),
  4516.           (size_t) folder_total(list),
  4517.           sizeof(FOLDER_S *),
  4518.           sort);
  4519. }
  4520.     
  4521.  
  4522.  
  4523.  
  4524.  
  4525.  
  4526. static CONTEXT_S *post_cntxt = NULL;
  4527.  
  4528. /*----------------------------------------------------------------------
  4529.     Verify and canonicalize news groups names. 
  4530.     Called from the message composer
  4531.  
  4532. Args:  given_group    -- List of groups typed by user
  4533.        expanded_group -- pointer to point to expanded list, which will be
  4534.              allocated here and freed in caller.  If this is
  4535.              NULL, don't attempt to validate.
  4536.        error          -- pointer to store error message
  4537.        fcc            -- pointer to point to fcc, which will be
  4538.              allocated here and freed in caller
  4539.  
  4540. Returns:  0 if all is OK
  4541.          -1 if addresses weren't valid
  4542.  
  4543. Test the given list of newstroups against those recognized by our nntp
  4544. servers.  Testing by actually trying to open the list is much cheaper, both
  4545. in bandwidth and memory, than yanking the whole list across the wire.
  4546.   ----*/
  4547. int
  4548. news_build(given_group, expanded_group, error, fcc)
  4549.     char     *given_group,
  4550.         **expanded_group,
  4551.         **error;
  4552.     BUILDER_ARG     *fcc;
  4553. {
  4554.     char     ng_error[90], *p1, *p2, *name, *end, *ep, **server;
  4555.       /* I've got no idea where 90 comes from */
  4556.     int          expanded_len = 0, num_in_error = 0, cnt_errs, we_cancel = 0;
  4557.     MAILSTREAM  *stream = NULL;
  4558.     struct ng_list {
  4559.     char  *groupname;
  4560.     NgCacheReturns  found;
  4561.     struct ng_list *next;
  4562.     }*nglist = NULL, **ntmpp, *ntmp;
  4563. #ifdef SENDNEWS
  4564.     static int no_servers = 0;
  4565. #endif
  4566.  
  4567.  
  4568.     dprint(2, (debugfile,
  4569.     "- news_build - (%s)\n", given_group ? given_group : "nul"));
  4570.  
  4571.     if(error)
  4572.       *error = NULL;
  4573.  
  4574.     /*------ parse given entries into a list ----*/
  4575.     ntmpp = &nglist;
  4576.     for(name = given_group; *name; name = end){
  4577.  
  4578.     /* find start of next group name */
  4579.         while(*name && (isspace(*name) || *name == ','))
  4580.       name++;
  4581.  
  4582.     /* find end of group name */
  4583.     end = name;
  4584.     while(*end && !isspace(*end) && *end != ',')
  4585.       end++;
  4586.  
  4587.         if(end != name){
  4588.         *ntmpp = (struct ng_list *)fs_get(sizeof(struct ng_list));
  4589.         (*ntmpp)->next      = NULL;
  4590.         (*ntmpp)->found     = NotChecked;
  4591.             (*ntmpp)->groupname = fs_get(end - name + 1);
  4592.             strncpy((*ntmpp)->groupname, name, end - name);
  4593.             (*ntmpp)->groupname[end - name] = '\0';
  4594.         ntmpp = &(*ntmpp)->next;
  4595.         if(!expanded_group)
  4596.           break;  /* no need to continue if just doing fcc */
  4597.         }
  4598.     }
  4599.  
  4600.     /*
  4601.      * If fcc is not set or is set to default, then replace it if
  4602.      * one of the recipient rules is in effect.
  4603.      */
  4604.     if(fcc){
  4605.     if((ps_global->fcc_rule == FCC_RULE_RECIP ||
  4606.         ps_global->fcc_rule == FCC_RULE_NICK_RECIP) &&
  4607.            (nglist && nglist->groupname)){
  4608.       if(fcc->tptr)
  4609.         fs_give((void **)&fcc->tptr);
  4610.  
  4611.       fcc->tptr = cpystr(nglist->groupname);
  4612.     }
  4613.     else if(!fcc->tptr) /* already default otherwise */
  4614.       fcc->tptr = cpystr(ps_global->VAR_DEFAULT_FCC);
  4615.     }
  4616.  
  4617.     if(!nglist){
  4618.     if(expanded_group)
  4619.       *expanded_group = cpystr("");
  4620.         return 0;
  4621.     }
  4622.  
  4623.     if(!expanded_group)
  4624.       return 0;
  4625.  
  4626. #ifdef    DEBUG
  4627.     for(ntmp = nglist; debug >= 9 && ntmp; ntmp = ntmp->next)
  4628.       dprint(9, (debugfile, "Parsed group: --[%s]--\n", ntmp->groupname));
  4629. #endif
  4630.  
  4631.     /* If we are doing validation */
  4632.     if(F_OFF(F_NO_NEWS_VALIDATION, ps_global)){
  4633.     int need_to_talk_to_server = 0;
  4634.  
  4635.     /*
  4636.      * First check our cache of validated newsgroups to see if we even
  4637.      * have to open a stream.
  4638.      */
  4639.     for(ntmp = nglist; ntmp; ntmp = ntmp->next){
  4640.         ntmp->found = chk_newsgrp_cache(ntmp->groupname);
  4641.         if(ntmp->found == NotInCache)
  4642.           need_to_talk_to_server++;
  4643.     }
  4644.  
  4645.     if(need_to_talk_to_server){
  4646.  
  4647. #ifdef SENDNEWS
  4648.       if(no_servers == 0)
  4649. #endif
  4650.         we_cancel = busy_alarm(1, "Validating newsgroup(s)", NULL, 1);
  4651.  
  4652.         /*
  4653.          * Build a stream to the first server that'll talk to us...
  4654.          */
  4655.         for(server = ps_global->VAR_NNTP_SERVER;
  4656.         server && *server && **server;
  4657.         server++){
  4658.         char name[MAXPATH];
  4659.  
  4660.         sprintf(name, "*{%s/nntp}", *server);
  4661.         if(stream = mail_open(stream, name, OP_HALFOPEN))
  4662.           break;
  4663.         }
  4664.  
  4665.         if(!server || !stream){
  4666.         if(error)
  4667. #ifdef SENDNEWS
  4668.         {
  4669.          /* don't say this over and over */
  4670.          if(no_servers == 0){
  4671.             if(!server || !*server || !**server)
  4672.               no_servers++;
  4673.  
  4674.             *error = cpystr(no_servers
  4675.                 ? "Can't validate groups.  No servers defined"
  4676.                 : "Can't validate groups.  No servers responding");
  4677.          }
  4678.         }
  4679. #else
  4680.           *error = cpystr((!server || !*server || !**server)
  4681.                 ? "No servers defined for posting to newsgroups"
  4682.                 : "Can't validate groups.  No servers responding");
  4683. #endif
  4684.         *expanded_group = cpystr(given_group);
  4685.         goto done;
  4686.         }
  4687.     }
  4688.  
  4689.     /*
  4690.      * Now, go thru the list, making sure we can at least open each one...
  4691.      */
  4692.     for(ntmp = nglist; ntmp; ntmp = ntmp->next){
  4693.         /*
  4694.          * It's faster and easier right now just to open the stream and
  4695.          * do our own finds than to use the current folder_exists()
  4696.          * interface...
  4697.          */
  4698.         if(ntmp->found == NotInCache){
  4699.         find_folder_list  = NULL;
  4700.         find_folder_count = 0L;
  4701.         context_find_all_bboard(NULL, stream, ntmp->groupname);
  4702.         ntmp->found = (find_folder_count > 0L) ? Found : Missing;
  4703.         }
  4704.         add_newsgrp_cache(ntmp->groupname, ntmp->found);
  4705.     }
  4706.  
  4707.     mail_close(stream);
  4708.     }
  4709.  
  4710.     /* figure length of string for matching groups */
  4711.     for(ntmp = nglist; ntmp; ntmp = ntmp->next){
  4712.       if(ntmp->found == Found || F_ON(F_NO_NEWS_VALIDATION, ps_global))
  4713.     expanded_len += strlen(ntmp->groupname) + 2;
  4714.       else
  4715.     num_in_error++;
  4716.     }
  4717.  
  4718.     /*
  4719.      * allocate and write the allowed, and error lists...
  4720.      */
  4721.     p1 = *expanded_group = fs_get((expanded_len + 1) * sizeof(char));
  4722.     if(error && num_in_error){
  4723.     cnt_errs = num_in_error;
  4724.     memset((void *)ng_error, 0, (size_t)90);
  4725.     sprintf(ng_error, "Unknown news group%s: ", plural(num_in_error));
  4726.     ep = ng_error + strlen(ng_error);
  4727.     }
  4728.     for(ntmp = nglist; ntmp; ntmp = ntmp->next){
  4729.     p2 = ntmp->groupname;
  4730.     if(ntmp->found == Found || F_ON(F_NO_NEWS_VALIDATION, ps_global)){
  4731.         while(*p2)
  4732.           *p1++ = *p2++;
  4733.  
  4734.         if(ntmp->next){
  4735.         *p1++ = ',';
  4736.         *p1++ = ' ';
  4737.         }
  4738.     }
  4739.     else if (error){
  4740.         while(*p2 && (ep - ng_error < 89))
  4741.           *ep++ = *p2++;
  4742.  
  4743.         if(--cnt_errs > 0 && (ep - ng_error < 87)){
  4744.         strcpy(ep, ", ");
  4745.         ep += 2;
  4746.         }
  4747.     }
  4748.     }
  4749.  
  4750.     *p1 = '\0';
  4751.  
  4752.     if(error && num_in_error)
  4753.       *error = cpystr(ng_error);
  4754.  
  4755. done:
  4756.     while(ntmp = nglist){
  4757.     nglist = nglist->next;
  4758.     fs_give((void **)&ntmp->groupname);
  4759.     fs_give((void **)&ntmp);
  4760.     }
  4761.  
  4762.     if(we_cancel)
  4763.       cancel_busy_alarm(0);
  4764.  
  4765.     return(num_in_error ? -1 : 0);
  4766. }
  4767.  
  4768.  
  4769. typedef struct ng_cache {
  4770.     char          *name;
  4771.     NgCacheReturns val;
  4772. }NgCache;
  4773. static NgCache *ng_cache_ptr;
  4774. #if defined(DOS) && !defined(_WINDOWS)
  4775. #define MAX_NGCACHE_ENTRIES 15
  4776. #else
  4777. #define MAX_NGCACHE_ENTRIES 40
  4778. #endif
  4779. /*
  4780.  * Simple newsgroup validity cache.  Opening a newsgroup to see if it
  4781.  * exists can be very slow on a heavily loaded NNTP server, so we cache
  4782.  * the results.
  4783.  */
  4784. NgCacheReturns
  4785. chk_newsgrp_cache(group)
  4786. char *group;
  4787. {
  4788.     register NgCache *ngp;
  4789.     
  4790.     for(ngp = ng_cache_ptr; ngp && ngp->name; ngp++){
  4791.     if(strcmp(group, ngp->name) == 0)
  4792.       return(ngp->val);
  4793.     }
  4794.  
  4795.     return NotInCache;
  4796. }
  4797.  
  4798.  
  4799. /*
  4800.  * Add an entry to the newsgroup validity cache.
  4801.  *
  4802.  * LRU entry is the one on the bottom, oldest on the top.
  4803.  * A slot has an entry in it if name is not NULL.
  4804.  */
  4805. void
  4806. add_newsgrp_cache(group, result)
  4807. char *group;
  4808. NgCacheReturns result;
  4809. {
  4810.     register NgCache *ngp;
  4811.     NgCache save_ngp;
  4812.  
  4813.     /* first call, initialize cache */
  4814.     if(!ng_cache_ptr){
  4815.     int i;
  4816.  
  4817.     ng_cache_ptr =
  4818.         (NgCache *)fs_get((MAX_NGCACHE_ENTRIES+1)*sizeof(NgCache));
  4819.     for(i = 0; i <= MAX_NGCACHE_ENTRIES; i++){
  4820.         ng_cache_ptr[i].name = NULL;
  4821.         ng_cache_ptr[i].val  = NotInCache;
  4822.     }
  4823.     ng_cache_ptr[MAX_NGCACHE_ENTRIES].val  = End;
  4824.     }
  4825.  
  4826.     if(chk_newsgrp_cache(group) == NotInCache){
  4827.     /* find first empty slot or End */
  4828.     for(ngp = ng_cache_ptr; ngp->name; ngp++)
  4829.       ;/* do nothing */
  4830.     if(ngp->val == End){
  4831.         /*
  4832.          * Cache is full, throw away top entry, move everything up,
  4833.          * and put new entry on the bottom.
  4834.          */
  4835.         ngp = ng_cache_ptr;
  4836.         if(ngp->name) /* just making sure */
  4837.           fs_give((void **)&ngp->name);
  4838.  
  4839.         for(; (ngp+1)->name; ngp++){
  4840.         ngp->name = (ngp+1)->name;
  4841.         ngp->val  = (ngp+1)->val;
  4842.         }
  4843.     }
  4844.     ngp->name = cpystr(group);
  4845.     ngp->val  = result;
  4846.     }
  4847.     else{
  4848.     /*
  4849.      * Move this entry from current location to last to preserve
  4850.      * LRU order.
  4851.      */
  4852.     for(ngp = ng_cache_ptr; ngp && ngp->name; ngp++){
  4853.         if(strcmp(group, ngp->name) == 0) /* found it */
  4854.           break;
  4855.     }
  4856.     save_ngp.name = ngp->name;
  4857.     save_ngp.val  = ngp->val;
  4858.     for(; (ngp+1)->name; ngp++){
  4859.         ngp->name = (ngp+1)->name;
  4860.         ngp->val  = (ngp+1)->val;
  4861.     }
  4862.     ngp->name = save_ngp.name;
  4863.     ngp->val  = save_ngp.val;
  4864.     }
  4865. }
  4866.  
  4867.  
  4868. void
  4869. free_newsgrp_cache()
  4870. {
  4871.     register NgCache *ngp;
  4872.  
  4873.     for(ngp = ng_cache_ptr; ngp && ngp->name; ngp++)
  4874.       fs_give((void **)&ngp->name);
  4875.     if(ng_cache_ptr)
  4876.       fs_give((void **)&ng_cache_ptr);
  4877. }
  4878.  
  4879.  
  4880. /*----------------------------------------------------------------------
  4881.     Browse list of newsgroups available for posting
  4882.  
  4883.   Called from composer when ^T is typed in newsgroups field
  4884.  
  4885. Args:    none
  4886.  
  4887. Returns: pointer to selected newsgroup, or NULL.
  4888.          Selector call in composer expects this to be alloc'd here.
  4889.  
  4890.   ----*/
  4891. char *
  4892. news_group_selector(error_mess)
  4893.     char **error_mess;
  4894. {
  4895.     FSTATE_S  post_state, *push_state;
  4896.     char     *post_folder;
  4897.     int       rc;
  4898.     char     *em;
  4899.  
  4900.     /* Coming back from composer */
  4901.     fix_windsize(ps_global);
  4902.     init_sigwinch();
  4903.  
  4904.     post_folder = fs_get((size_t)500);
  4905.  
  4906.     /*--- build the post_cntxt -----*/
  4907.     em = get_post_list(ps_global->VAR_NNTP_SERVER);
  4908.     if(em != NULL){
  4909.         if(error_mess != NULL)
  4910.           *error_mess = em;
  4911.  
  4912.     cancel_busy_alarm(-1);
  4913.         return(NULL);
  4914.     }
  4915.  
  4916.     /*----- Call the browser -------*/
  4917.     push_state = fs;
  4918.     rc = folder_lister(ps_global, PostNews, post_cntxt, NULL,
  4919.                        post_folder, NULL, post_cntxt, &post_state);
  4920.     fs = push_state;
  4921.  
  4922.     cancel_busy_alarm(-1);
  4923.  
  4924.     if(rc <= 0)
  4925.       return(NULL);
  4926.  
  4927.     return(post_folder);
  4928. }
  4929.  
  4930.  
  4931.  
  4932. /*----------------------------------------------------------------------
  4933.     Get the list of news groups that are possible for posting
  4934.  
  4935. Args: post_host -- host name for posting
  4936.  
  4937. Returns NULL if list is retrieved, pointer to error message if failed
  4938.  
  4939. This is kept in a standards "CONTEXT" for a acouple of reasons. First
  4940. it makes it very easy to use the folder browser to display the
  4941. newsgroup for selection on ^T from the composer. Second it will allow
  4942. the same mechanism to be used for all folder lists on memory tight
  4943. systems like DOS. The list is kept for the life of the session because
  4944. fetching it is a expensive. 
  4945.  
  4946.  ----*/
  4947. char *
  4948. get_post_list(post_host)
  4949.      char **post_host;
  4950. {
  4951.     char *post_context_string;
  4952.  
  4953.     if(!post_host || !post_host[0]) {
  4954.         /* BUG should assume inews and get this from active file */
  4955.         return("Can't post messages, NNTP server needs to be configured");
  4956.     }
  4957.  
  4958.     if(!post_cntxt){
  4959.     int we_cancel;
  4960.  
  4961.     we_cancel
  4962.         = busy_alarm(1, "Getting full list of groups for posting", NULL, 0);
  4963.  
  4964.         post_context_string = fs_get(strlen(post_host[0]) + 20);
  4965.         sprintf(post_context_string, "*{%s/nntp}[]", post_host[0]);
  4966.         
  4967.         post_cntxt          = new_context(post_context_string);
  4968.  
  4969.         post_cntxt->use    |= CNTXT_FINDALL | CNTXT_NOFIND; 
  4970.         post_cntxt->next    = NULL;
  4971.  
  4972.         find_folders_in_context(NULL, post_cntxt, NULL);
  4973.     if(we_cancel)
  4974.       cancel_busy_alarm(-1);
  4975.     }
  4976.     return(NULL);
  4977. }
  4978.  
  4979.  
  4980. #ifdef _WINDOWS
  4981. /*----------------------------------------------------------------------
  4982.      MSWin scroll callback.  Called during scroll message processing.
  4983.          
  4984.  
  4985.  
  4986.   Args: cmd - what type of scroll operation.
  4987.     scroll_pos - paramter for operation.  
  4988.             used as position for SCROLL_TO operation.
  4989.  
  4990.   Returns: TRUE - did the scroll operation.
  4991.        FALSE - was not able to do the scroll operation.
  4992.  ----*/
  4993. int
  4994. folder_scroll_callback (cmd, scroll_pos)
  4995. int    cmd;
  4996. long    scroll_pos;
  4997. {
  4998.     static short bad_timing = 0;
  4999.     int    rv;
  5000.  
  5001.     if(bad_timing)
  5002.       return(FALSE);
  5003.     else
  5004.       bad_timing = TRUE;
  5005.  
  5006.     switch (cmd) {
  5007.       case MSWIN_KEY_SCROLLUPLINE:
  5008.     rv = folder_scroll_down(1L);
  5009.     break;
  5010.  
  5011.       case MSWIN_KEY_SCROLLDOWNLINE:
  5012.     rv = folder_scroll_up(1L);
  5013.     break;
  5014.  
  5015.       case MSWIN_KEY_SCROLLUPPAGE:
  5016.     rv = folder_scroll_down(fs->display_rows);
  5017.     break;
  5018.  
  5019.       case MSWIN_KEY_SCROLLDOWNPAGE:
  5020.     rv = folder_scroll_up(fs->display_rows);
  5021.     break;
  5022.  
  5023.       case MSWIN_KEY_SCROLLTO:
  5024.     rv =  folder_scroll_to_pos(scroll_pos);
  5025.     break;
  5026.     }
  5027.  
  5028.     if(rv)
  5029.       display_folder(fs, fs->context, fs->folder_index, NULL, -1);
  5030.  
  5031.     bad_timing = FALSE;
  5032.     return(rv);
  5033. }
  5034. #endif    /* _WINDOWS */
  5035.